* @author Henrik Maier */ class Extension { /** * @var ViewRenderer */ protected $viewRenderer; /** * @var Smarty */ protected $smarty; /** * @param ViewRenderer $viewRenderer * @param Smarty $smarty */ public function __construct($viewRenderer, $smarty) { $this->viewRenderer = $viewRenderer; $smarty = $this->smarty = $smarty; $smarty->registerPlugin('function', 'path', [$this, 'functionPath']); $smarty->registerPlugin('function', 'url', [$this, 'functionUrl']); $smarty->registerPlugin('function', 'set', [$this, 'functionSet']); $smarty->registerPlugin('function', 'meta', [$this, 'functionMeta']); $smarty->registerPlugin('function', 'registerJsFile', [$this, 'functionRegisterJsFile']); $smarty->registerPlugin('function', 'registerCssFile', [$this, 'functionRegisterCssFile']); $smarty->registerPlugin('block', 'title', [$this, 'blockTitle']); $smarty->registerPlugin('block', 'description', [$this, 'blockDescription']); $smarty->registerPlugin('block', 'registerJs', [$this, 'blockJavaScript']); $smarty->registerPlugin('block', 'registerCss', [$this, 'blockCss']); $smarty->registerPlugin('compiler', 'use', [$this, 'compilerUse']); $smarty->registerPlugin('modifier', 'void', [$this, 'modifierVoid']); } /** * Smarty template function to get relative URL for using in links * * Usage is the following: * * {path route='blog/view' alias=$post.alias user=$user.id} * * where route is Yii route and the rest of parameters are passed as is. * * @param array $params * @param \Smarty_Internal_Template $template * * @return string */ public function functionPath($params, \Smarty_Internal_Template $template) { if (!isset($params['route'])) { trigger_error("path: missing 'route' parameter"); } array_unshift($params, $params['route']) ; unset($params['route']); return Url::to($params); } /** * Smarty template function to get absolute URL for using in links * * Usage is the following: * * {url route='blog/view' alias=$post.alias user=$user.id} * * where route is Yii route and the rest of parameters are passed as is. * * @param array $params * @param \Smarty_Internal_Template $template * * @return string */ public function functionUrl($params, \Smarty_Internal_Template $template) { if (!isset($params['route'])) { trigger_error("path: missing 'route' parameter"); } array_unshift($params, $params['route']) ; unset($params['route']); return Url::to($params, true); } /** * Smarty compiler function plugin * Usage is the following: * * {use class="app\assets\AppAsset"} * {use class="yii\helpers\Html"} * {use class='yii\widgets\ActiveForm' type='block'} * {use class='@app\widgets\MyWidget' as='my_widget' type='function'} * * Supported attributes: class, as, type. Type defaults to 'static'. * * @param $params * @param \Smarty_Internal_Template $template * @return string * @note Even though this method is public it should not be called directly. */ public function compilerUse($params, $template) { if (!isset($params['class'])) { trigger_error("use: missing 'class' parameter"); } // Compiler plugin parameters may include quotes, so remove them foreach ($params as $key => $value) { $params[$key] = trim($value, '\'""'); } $class = $params['class']; $alias = ArrayHelper::getValue($params, 'as', StringHelper::basename($params['class'])); $type = ArrayHelper::getValue($params, 'type', 'static'); // Register the class during compile time $this->smarty->registerClass($alias, $class); if ($type === 'block') { // Register widget tag during compile time $this->viewRenderer->widgets['blocks'][$alias] = $class; $this->smarty->registerPlugin('block', $alias, [$this->viewRenderer, '_widget_block__' . $alias]); // Inject code to re-register widget tag during run-time return <<default_template_handler_func[0]; \$viewRenderer->widgets['blocks']['$alias'] = '$class'; try { \$_smarty_tpl->registerPlugin('block', '$alias', [\$viewRenderer, '_widget_block__$alias']); } catch (SmartyException \$e) { /* Ignore already registered exception during first execution after compilation */ } ?> PHP; } elseif ($type === 'function') { // Register widget tag during compile time $this->viewRenderer->widgets['functions'][$alias] = $class; $this->smarty->registerPlugin('function', $alias, [$this->viewRenderer, '_widget_function__' . $alias]); // Inject code to re-register widget tag during run-time return <<default_template_handler_func[0]; \$viewRenderer->widgets['functions']['$alias'] = '$class'; try { \$_smarty_tpl->registerPlugin('function', '$alias', [\$viewRenderer, '_widget_function__$alias']); } catch (SmartyException \$e) { /* Ignore already registered exception during first execution after compilation */ } ?> PHP; } } /** * Smarty modifier plugin * Converts any output to void * @param mixed $arg * @return string * @note Even though this method is public it should not be called directly. */ public function modifierVoid($arg) { return; } /** * Smarty function plugin * Usage is the following: * * {set title="My Page"} * {set theme="frontend"} * {set layout="main.tpl"} * * Supported attributes: title, theme, layout * * @param $params * @param \Smarty_Internal_Template $template * @return string * @note Even though this method is public it should not be called directly. */ public function functionSet($params, $template) { if (isset($params['title'])) { $template->tpl_vars['this']->value->title = Yii::$app->getView()->title = ArrayHelper::remove($params, 'title'); } if (isset($params['theme'])) { $template->tpl_vars['this']->value->theme = Yii::$app->getView()->theme = ArrayHelper::remove($params, 'theme'); } if (isset($params['layout'])) { Yii::$app->controller->layout = ArrayHelper::remove($params, 'layout'); } // We must have consumed all allowed parameters now, otherwise raise error if (!empty($params)) { trigger_error('set: Unsupported parameter attribute'); } } /** * Smarty function plugin * Usage is the following: * * {meta keywords="Yii,PHP,Smarty,framework"} * * Supported attributes: any; all attributes are passed as * parameter array to Yii's registerMetaTag function. * * @param $params * @param \Smarty_Internal_Template $template * @return string * @note Even though this method is public it should not be called directly. */ public function functionMeta($params, $template) { $key = isset($params['name']) ? $params['name'] : null; Yii::$app->getView()->registerMetaTag($params, $key); } /** * Smarty block function plugin * Usage is the following: * * {title} Web Site Login {/title} * * Supported attributes: none. * * @param $params * @param $content * @param \Smarty_Internal_Template $template * @param $repeat * @return string * @note Even though this method is public it should not be called directly. */ public function blockTitle($params, $content, $template, &$repeat) { if ($content !== null) { Yii::$app->getView()->title = $content; } } /** * Smarty block function plugin * Usage is the following: * * {description} * The text between the opening and closing tags is added as * meta description tag to the page output. * {/description} * * Supported attributes: none. * * @param $params * @param $content * @param \Smarty_Internal_Template $template * @param $repeat * @return string * @note Even though this method is public it should not be called directly. */ public function blockDescription($params, $content, $template, &$repeat) { if ($content !== null) { // Clean-up whitespace and newlines $content = preg_replace('/\s+/', ' ', trim($content)); Yii::$app->getView()->registerMetaTag(['name' => 'description', 'content' => $content], 'description'); } } /** * Smarty function plugin * Usage is the following: * * {registerJsFile url='http://maps.google.com/maps/api/js?sensor=false' position='POS_END'} * * Supported attributes: url, key, depends, position and valid HTML attributes for the script tag. * Refer to Yii documentation for details. * The position attribute is passed as text without the class prefix. * Default is 'POS_END'. * * @param $params * @param \Smarty_Internal_Template $template * @return string * @note Even though this method is public it should not be called directly. */ public function functionRegisterJsFile($params, $template) { if (!isset($params['url'])) { trigger_error("registerJsFile: missing 'url' parameter"); } $url = ArrayHelper::remove($params, 'url'); $key = ArrayHelper::remove($params, 'key', null); $depends = ArrayHelper::remove($params, 'depends', null); if (isset($params['position'])) $params['position'] = $this->getViewConstVal($params['position'], View::POS_END); Yii::$app->getView()->registerJsFile($url, $depends, $params, $key); } /** * Smarty block function plugin * Usage is the following: * * {registerJs key='show' position='POS_LOAD'} * $("span.show").replaceWith('
'); * {/registerJs} * * Supported attributes: key, position. Refer to Yii documentation for details. * The position attribute is passed as text without the class prefix. * Default is 'POS_READY'. * * @param $params * @param $content * @param \Smarty_Internal_Template $template * @param $repeat * @return string * @note Even though this method is public it should not be called directly. */ public function blockJavaScript($params, $content, $template, &$repeat) { if ($content !== null) { $key = isset($params['key']) ? $params['key'] : null; $position = isset($params['position']) ? $params['position'] : null; Yii::$app->getView()->registerJs($content, $this->getViewConstVal($position, View::POS_READY), $key); } } /** * Smarty function plugin * Usage is the following: * * {registerCssFile url='@assets/css/normalizer.css'} * * Supported attributes: url, key, depends and valid HTML attributes for the link tag. * Refer to Yii documentation for details. * * @param $params * @param \Smarty_Internal_Template $template * @return string * @note Even though this method is public it should not be called directly. */ public function functionRegisterCssFile($params, $template) { if (!isset($params['url'])) { trigger_error("registerCssFile: missing 'url' parameter"); } $url = ArrayHelper::remove($params, 'url'); $key = ArrayHelper::remove($params, 'key', null); $depends = ArrayHelper::remove($params, 'depends', null); Yii::$app->getView()->registerCssFile($url, $depends, $params, $key); } /** * Smarty block function plugin * Usage is the following: * * {registerCss} * div.header { * background-color: #3366bd; * color: white; * } * {/registerCss} * * Supported attributes: key and valid HTML attributes for the style tag. * Refer to Yii documentation for details. * * @param $params * @param $content * @param \Smarty_Internal_Template $template * @param $repeat * @return string * @note Even though this method is public it should not be called directly. */ public function blockCss($params, $content, $template, &$repeat) { if ($content !== null) { $key = isset($params['key']) ? $params['key'] : null; Yii::$app->getView()->registerCss($content, $params, $key); } } /** * Helper function to convert a textual constant identifier to a View class * integer constant value. * * @param string $string Constant identifier name * @param integer $default Default value * @return mixed */ protected function getViewConstVal($string, $default) { $val = @constant('yii\web\View::' . $string); return isset($val) ? $val : $default; } }