diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 35e8ebde9b7..6dd800e1911 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.50 under development ------------------------ +- Enh #20001: Improve `View` to allow ld+json and noscript tags (pgaultier) - Bug #19925: Improved PHP version check when handling MIME types (schmunk42) diff --git a/framework/web/View.php b/framework/web/View.php index f2c19fc721f..04d918ca613 100644 --- a/framework/web/View.php +++ b/framework/web/View.php @@ -11,6 +11,7 @@ use yii\base\InvalidConfigException; use yii\helpers\ArrayHelper; use yii\helpers\Html; +use yii\helpers\Json; use yii\helpers\Url; /** @@ -130,6 +131,16 @@ class View extends \yii\base\View * @see registerJsFile() */ public $jsFiles = []; + /** + * @var array list of registered json+ld blocks + * @see registerLdJson() + */ + public $ldJson = []; + /** + * @var array list of registered noscript tags + * @see registerNoscriptTag() + */ + public $noscriptTags = []; private $_assetManager; @@ -256,6 +267,8 @@ public function clear() $this->js = []; $this->jsFiles = []; $this->assetBundles = []; + $this->ldJson = []; + $this->noscriptTags = []; } /** @@ -401,6 +414,74 @@ public function registerLinkTag($options, $key = null) } } + /** + * Registers a noscript tag. + * + * For example, a noscript tag for a tracking pixel + * can be added like the following: + * + * ```php + * $view->registerNoscriptTag(''); + * ``` + * + * which will result in the following HTML: ``. + * + * @param array $options the HTML attributes for the link tag. + * @param int $position the position at which the noscript tag should be inserted + * @param string|null $key the key that identifies the link tag. If two link tags are registered + * with the same key, the latter will overwrite the former. If this is null, the new link tag + * will be appended to the existing ones. + */ + public function registerNoscriptTag($content, $options, $position = self::POS_HEAD, $key = null) + { + if ($key === null) { + $this->noscriptTags[$position][] = Html::tag('noscript', $content, $options); + } else { + $this->noscriptTags[$position][$key] = Html::tag('noscript', $content, $options); + } + } + + /** + * Registers a ld+json script tag. + * + * For example, a ld+json script tag for a custom video object + * can be added like the following: + * + * ```php + * $ldJson = [ + * '@context' => 'https://schema.org', + * '@type' => 'VideoObject', + * 'name' => $name, + * 'description' => $description, + * 'url' => $url + * ]; + * $view->registerLdJson($ldJson); + * ``` + * + * which will result in the following HTML: + * ```html + * + * ```. + * + * **Note:** All registered ld+json script tags which should be rendered in the head section will be merged into one script tag in an array. + * + * @param array $ldJson the LD+JSON object to be registered + * @param int $position the position at which the LD+JSON object should be inserted + * @param string|null $key the key that identifies the LD+JSON tag. If two objects are registered + * with the same key, the latter will overwrite the former. If this is null, the new link tag + * will be appended to the existing ones. + */ + public function registerLdJson($ldJson, $position = self::POS_HEAD, $key = null) + { + if ($key === null) { + $this->ldJson[$position][] = Html::script(Json::encode($ldJson), ['type' => 'application/ld+json']); + } else { + $this->ldJson[$position][$key] = Html::script(Json::encode($ldJson), ['type' => 'application/ld+json']); + } + } + /** * Registers a CSS code block. * @param string $css the content of the CSS code block to be registered @@ -599,10 +680,15 @@ protected function renderHeadHtml() if (!empty($this->metaTags)) { $lines[] = implode("\n", $this->metaTags); } - if (!empty($this->linkTags)) { $lines[] = implode("\n", $this->linkTags); } + if (!empty($this->noscriptTags[self::POS_HEAD])) { + $lines[] = implode("\n", $this->noscriptTags[self::POS_HEAD]); + } + if (!empty($this->ldJson[self::POS_HEAD])) { + $lines[] = implode("\n", $this->ldJson[self::POS_HEAD]); + } if (!empty($this->cssFiles)) { $lines[] = implode("\n", $this->cssFiles); } @@ -627,6 +713,12 @@ protected function renderHeadHtml() protected function renderBodyBeginHtml() { $lines = []; + if (!empty($this->ldJson[self::POS_BEGIN])) { + $lines[] = implode("\n", $this->ldJson[self::POS_BEGIN]); + } + if (!empty($this->noscriptTags[self::POS_BEGIN])) { + $lines[] = implode("\n", $this->noscriptTags[self::POS_BEGIN]); + } if (!empty($this->jsFiles[self::POS_BEGIN])) { $lines[] = implode("\n", $this->jsFiles[self::POS_BEGIN]); } @@ -652,7 +744,12 @@ protected function renderBodyEndHtml($ajaxMode) if (!empty($this->jsFiles[self::POS_END])) { $lines[] = implode("\n", $this->jsFiles[self::POS_END]); } - + if (!empty($this->ldJson[self::POS_END])) { + $lines[] = implode("\n", $this->ldJson[self::POS_END]); + } + if (!empty($this->noscriptTags[self::POS_END])) { + $lines[] = implode("\n", $this->noscriptTags[self::POS_END]); + } if ($ajaxMode) { $scripts = []; if (!empty($this->js[self::POS_END])) {