Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Template\SmartyPlugin;
|
||||
|
||||
use Smarty_Internal_Template;
|
||||
use Xentral\Components\Template\Exception\TemplateException;
|
||||
|
||||
final class EscapePlugin
|
||||
{
|
||||
/** @var string $charset */
|
||||
private $charset = 'UTF-8';
|
||||
|
||||
/**
|
||||
* @example {escape format='html'}{$evilString}{/escape}
|
||||
*
|
||||
* Default format is 'html'
|
||||
*
|
||||
* @param array $params
|
||||
* @param mixed $content
|
||||
* @param Smarty_Internal_Template $template
|
||||
* @param bool $repeat
|
||||
*
|
||||
* @throws TemplateException On missing params
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function compileEscapeBlock($params, $content, $template, &$repeat)
|
||||
{
|
||||
// Only output on closing tag @see https://www.smarty.net/docs/en/plugins.block.functions.tpl
|
||||
if ($repeat === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$format = isset($params['format']) ? $params['format'] : 'html';
|
||||
|
||||
switch ($format) {
|
||||
case 'none':
|
||||
case 'null':
|
||||
return $content;
|
||||
break;
|
||||
|
||||
case 'entitites':
|
||||
return $this->escapeHtmlEntities($content);
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
return $this->escapeQuotes($content);
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
return $this->escapeUrl($content);
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
default:
|
||||
return $this->escapeHtml($content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @example {escapeHtml}{$evilString}{/escapeHtml}
|
||||
*
|
||||
* @param array $params
|
||||
* @param mixed $content
|
||||
* @param Smarty_Internal_Template $template
|
||||
* @param bool $repeat
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEscapeHtmlBlock($params, $content, $template, &$repeat)
|
||||
{
|
||||
// Only output on closing tag @see https://www.smarty.net/docs/en/plugins.block.functions.tpl
|
||||
if ($repeat === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->escapeHtml($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example {$evilString|escape:'html'}
|
||||
*
|
||||
* @param string $content
|
||||
* @param string $format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEscapeModifier($content, $format = 'html')
|
||||
{
|
||||
switch ($format) {
|
||||
case 'none':
|
||||
case 'null':
|
||||
return $content;
|
||||
break;
|
||||
|
||||
case 'entitites':
|
||||
return $this->escapeHtmlEntities($content);
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
return $this->escapeQuotes($content);
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
return $this->escapeUrl($content);
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
default:
|
||||
return $this->escapeHtml($content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @example {$string|escapeEntities}
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEscapeEntitiesModifier($content)
|
||||
{
|
||||
return $this->escapeHtmlEntities($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes unescaped single quotes
|
||||
*
|
||||
* @example {$quotedString|escapeQuotes}
|
||||
*
|
||||
* @param mixed $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEscapeQuotesModifier($content)
|
||||
{
|
||||
return $this->escapeQuotes($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example {$evilString|escapeHtml}
|
||||
*
|
||||
* @param mixed $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEscapeHtmlModifier($content)
|
||||
{
|
||||
return $this->escapeHtml($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example {$url|escapeUrl}
|
||||
*
|
||||
* @param mixed $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEscapeUrlModifier($content)
|
||||
{
|
||||
return $this->escapeUrl($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function escapeHtml($string)
|
||||
{
|
||||
return htmlspecialchars($string, ENT_QUOTES, $this->charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function escapeHtmlEntities($string)
|
||||
{
|
||||
return htmlentities($string, ENT_QUOTES, $this->charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function escapeQuotes($string)
|
||||
{
|
||||
return preg_replace("%(?<!\\\\)'%", "\\'", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function escapeUrl($string)
|
||||
{
|
||||
return rawurlencode($string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Template\SmartyPlugin;
|
||||
|
||||
use Smarty_Internal_Template;
|
||||
use Smarty_Template_Source;
|
||||
use Xentral\Components\Template\Exception\TemplateException;
|
||||
|
||||
final class TranslationPlugin
|
||||
{
|
||||
/**
|
||||
* @todo Add TranslationService
|
||||
* @var null $translator
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
/** @var array $resourceNamespaces */
|
||||
private $resourceNamespaces = [];
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param Smarty_Internal_Template $template
|
||||
*
|
||||
* @throws TemplateException If 'key' param is not set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function compileNamespaceFunction($params, $template)
|
||||
{
|
||||
if (empty($params['key'])) {
|
||||
$sourceName = $this->getResourceSourceName($template);
|
||||
throw new TemplateException(sprintf(
|
||||
'Template error: Required parameter "key" is missing in {namespace} function. Source: %s', $sourceName
|
||||
));
|
||||
}
|
||||
|
||||
// Store which namespace is used in which template
|
||||
$resource = $template->source->resource;
|
||||
$this->addResourceNamespace($resource, $params['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param mixed $content
|
||||
* @param Smarty_Internal_Template $template
|
||||
* @param bool $repeat
|
||||
*
|
||||
* @throws TemplateException On missing params
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function compileTranslateBlock($params, $content, $template, &$repeat)
|
||||
{
|
||||
// Only output on closing tag @see https://www.smarty.net/docs/en/plugins.block.functions.tpl
|
||||
if ($repeat === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($params['key'])) {
|
||||
throw new TemplateException(sprintf(
|
||||
'Template error: Required parameter "key" is missing in {translate} block. Source: %s',
|
||||
$template->source->resource
|
||||
));
|
||||
}
|
||||
|
||||
if (!empty($params['namespace'])) {
|
||||
$namespace = $params['namespace'];
|
||||
} else {
|
||||
$namespace = $this->determineResourceNamespace($template);
|
||||
}
|
||||
|
||||
if (empty($namespace)) {
|
||||
$sourceName = $this->getResourceSourceName($template);
|
||||
throw new TemplateException(sprintf(
|
||||
'Template error: Namespace for translation could not be determined. Source: %s', $sourceName
|
||||
));
|
||||
}
|
||||
|
||||
// @todo Use TranslationService
|
||||
return (string)$content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Smarty_Internal_Template $template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getResourceSourceName($template)
|
||||
{
|
||||
$source = $template->source;
|
||||
if ($source->type === 'file') {
|
||||
return 'File ' . $source->filepath;
|
||||
}
|
||||
|
||||
return sprintf('Resource %s:%s', $source->type, $source->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resource
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isResourceNamespaceDefined($resource)
|
||||
{
|
||||
return isset($this->resourceNamespaces[$resource]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resource
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getResourceNamespace($resource)
|
||||
{
|
||||
return $this->resourceNamespaces[$resource];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Smarty_Internal_Template $template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function determineResourceNamespace($template)
|
||||
{
|
||||
$resource = $template->source->resource;
|
||||
if ($this->isResourceNamespaceDefined($resource)) {
|
||||
return $this->getResourceNamespace($resource);
|
||||
}
|
||||
|
||||
$namespace = $this->findResourceNamespaceFromParents($template->inheritance->sources);
|
||||
$this->addResourceNamespace($resource, $namespace);
|
||||
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|Smarty_Template_Source[] $parents
|
||||
*
|
||||
* @return string|null Namespace
|
||||
*/
|
||||
private function findResourceNamespaceFromParents($parents)
|
||||
{
|
||||
foreach ($parents as $source) {
|
||||
$resource = $source->resource;
|
||||
if ($this->isResourceNamespaceDefined($resource)) {
|
||||
return $this->resourceNamespaces[$resource];
|
||||
}
|
||||
}
|
||||
|
||||
return 'default';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $resource
|
||||
* @param string $namespace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addResourceNamespace($resource, $namespace)
|
||||
{
|
||||
if ($this->isResourceNamespaceDefined($resource)) {
|
||||
throw new TemplateException(sprintf(
|
||||
'Template resource namespace for resource "%s" is already defined', $resource
|
||||
));
|
||||
}
|
||||
|
||||
$this->resourceNamespaces[$resource] = $namespace;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user