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])) {