123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- <?php
- namespace Grav\Common\Twig;
- use Grav\Common\Grav;
- use Grav\Common\Utils;
- use Grav\Common\Markdown\Parsedown;
- use Grav\Common\Markdown\ParsedownExtra;
- use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
- /**
- * The Twig extension adds some filters and functions that are useful for Grav
- *
- * @author RocketTheme
- * @license MIT
- */
- class TwigExtension extends \Twig_Extension
- {
- protected $grav;
- protected $debugger;
- protected $config;
- public function __construct()
- {
- $this->grav = Grav::instance();
- $this->debugger = isset($this->grav['debugger']) ? $this->grav['debugger'] : null;
- $this->config = $this->grav['config'];
- }
- /**
- * Returns extension name.
- *
- * @return string
- */
- public function getName()
- {
- return 'GravTwigExtension';
- }
- /**
- * Register some standard globals
- *
- * @return array
- */
- public function getGlobals()
- {
- return array(
- 'grav' => $this->grav,
- );
- }
- /**
- * Return a list of all filters.
- *
- * @return array
- */
- public function getFilters()
- {
- return [
- new \Twig_SimpleFilter('*ize', [$this,'inflectorFilter']),
- new \Twig_SimpleFilter('absolute_url', [$this, 'absoluteUrlFilter']),
- new \Twig_SimpleFilter('contains', [$this, 'containsFilter']),
- new \Twig_SimpleFilter('defined', [$this, 'definedDefaultFilter']),
- new \Twig_SimpleFilter('ends_with', [$this, 'endsWithFilter']),
- new \Twig_SimpleFilter('fieldName', [$this,'fieldNameFilter']),
- new \Twig_SimpleFilter('ksort', [$this,'ksortFilter']),
- new \Twig_SimpleFilter('ltrim', [$this, 'ltrimFilter']),
- new \Twig_SimpleFilter('markdown', [$this, 'markdownFilter']),
- new \Twig_SimpleFilter('md5', [$this,'md5Filter']),
- new \Twig_SimpleFilter('nicetime', [$this, 'nicetimeFilter']),
- new \Twig_SimpleFilter('randomize', [$this,'randomizeFilter']),
- new \Twig_SimpleFilter('modulus', [$this,'modulusFilter']),
- new \Twig_SimpleFilter('rtrim', [$this, 'rtrimFilter']),
- new \Twig_SimpleFilter('safe_email', [$this,'safeEmailFilter']),
- new \Twig_SimpleFilter('safe_truncate', ['\Grav\Common\Utils','safeTruncate']),
- new \Twig_SimpleFilter('safe_truncate_html', ['\Grav\Common\Utils','safeTruncateHTML']),
- new \Twig_SimpleFilter('sort_by_key', [$this,'sortByKeyFilter']),
- new \Twig_SimpleFilter('starts_with', [$this, 'startsWithFilter']),
- new \Twig_SimpleFilter('t', [$this, 'translate']),
- new \Twig_SimpleFilter('ta', [$this, 'translateArray']),
- new \Twig_SimpleFilter('truncate', ['\Grav\Common\Utils','truncate']),
- new \Twig_SimpleFilter('truncate_html', ['\Grav\Common\Utils','truncateHTML']),
- ];
- }
- /**
- * Return a list of all functions.
- *
- * @return array
- */
- public function getFunctions()
- {
- return [
- new \Twig_SimpleFunction('array', [$this, 'arrayFunc']),
- new \Twig_simpleFunction('authorize', [$this, 'authorize']),
- new \Twig_SimpleFunction('debug', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]),
- new \Twig_SimpleFunction('dump', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]),
- new \Twig_SimpleFunction('gist', [$this, 'gistFunc']),
- new \Twig_simpleFunction('random_string', [$this, 'randomStringFunc']),
- new \Twig_SimpleFunction('repeat', [$this, 'repeatFunc']),
- new \Twig_SimpleFunction('string', [$this, 'stringFunc']),
- new \Twig_simpleFunction('t', [$this, 'translate']),
- new \Twig_simpleFunction('ta', [$this, 'translateArray']),
- new \Twig_SimpleFunction('url', [$this, 'urlFunc']),
- new \Twig_SimpleFunction('evaluate', [$this, 'evaluateFunc']),
- ];
- }
- /**
- * Filters field name by changing dot notation into array notation.
- *
- * @param string $str
- * @return string
- */
- public function fieldNameFilter($str)
- {
- $path = explode('.', $str);
- return array_shift($path) . ($path ? '[' . implode('][', $path) . ']' : '');
- }
- /**
- * Protects email address.
- *
- * @param string $str
- * @return string
- */
- public function safeEmailFilter($str)
- {
- $email = '';
- $str_len = strlen($str);
- for ($i = 0; $i < $str_len; $i++) {
- $email .= "&#" . ord($str[$i]). ";";
- }
- return $email;
- }
- /**
- * Returns array in a random order.
- *
- * @param array $original
- * @param int $offset Can be used to return only slice of the array.
- * @return array
- */
- public function randomizeFilter($original, $offset = 0)
- {
- if (!is_array($original)) {
- return $original;
- }
- if ($original instanceof \Traversable) {
- $original = iterator_to_array($original, false);
- }
- $sorted = [];
- $random = array_slice($original, $offset);
- shuffle($random);
- $sizeOf = sizeof($original);
- for ($x=0; $x < $sizeOf; $x++) {
- if ($x < $offset) {
- $sorted[] = $original[$x];
- } else {
- $sorted[] = array_shift($random);
- }
- }
- return $sorted;
- }
- /**
- * Returns the modulus of an integer
- *
- * @param int $number
- * @param int $divider
- * @param array $items array of items to select from to return
- * @return int
- */
- public function modulusFilter($number, $divider, $items = null)
- {
- if (is_string($number)) {
- $number = strlen($number);
- }
- $remainder = $number % $divider;
- if (is_array($items)) {
- if (isset($items[$remainder])) {
- return $items[$remainder];
- } else {
- return $items[0];
- }
- }
- return $remainder;
- }
- /**
- * Inflector supports following notations:
- *
- * {{ 'person'|pluralize }} => people
- * {{ 'shoes'|singularize }} => shoe
- * {{ 'welcome page'|titleize }} => "Welcome Page"
- * {{ 'send_email'|camelize }} => SendEmail
- * {{ 'CamelCased'|underscorize }} => camel_cased
- * {{ 'Something Text'|hyphenize }} => something-text
- * {{ 'something_text_to_read'|humanize }} => "Something text to read"
- * {{ '181'|monthize }} => 6
- * {{ '10'|ordinalize }} => 10th
- *
- * @param string $action
- * @param string $data
- * @param int $count
- * @return mixed
- */
- public function inflectorFilter($action, $data, $count = null)
- {
- $action = $action.'ize';
- $inflector = $this->grav['inflector'];
- if (in_array(
- $action,
- ['titleize','camelize','underscorize','hyphenize', 'humanize','ordinalize','monthize']
- )) {
- return $inflector->$action($data);
- } elseif (in_array($action, ['pluralize','singularize'])) {
- if ($count) {
- return $inflector->$action($data, $count);
- } else {
- return $inflector->$action($data);
- }
- } else {
- return $data;
- }
- }
- /**
- * Return MD5 hash from the input.
- *
- * @param string $str
- * @return string
- */
- public function md5Filter($str)
- {
- return md5($str);
- }
- /**
- * Sorts a collection by key
- *
- * @param array $input
- * @param string $filter
- * @param array|int $direction
- *
- * @return string
- */
- public function sortByKeyFilter(array $input, $filter, $direction = SORT_ASC)
- {
- $output = [];
- if (!$input) {
- return $output;
- }
- foreach ($input as $key => $row) {
- $output[$key] = $row[$filter];
- }
- array_multisort($output, $direction, $input);
- return $input;
- }
- /**
- * Return ksorted collection.
- *
- * @param array $array
- * @return array
- */
- public function ksortFilter(array $array)
- {
- ksort($array);
- return $array;
- }
- /**
- * determine if a string contains another
- *
- * @param String $haystack
- * @param String $needle
- *
- * @return boolean
- */
- public function containsFilter($haystack, $needle)
- {
- return (strpos($haystack, $needle) !== false);
- }
- /**
- * displays a facebook style 'time ago' formatted date/time
- *
- * @param $date
- * @param $long_strings
- * @param String
- *
- * @return boolean
- */
- public function nicetimeFilter($date, $long_strings = true)
- {
- if (empty($date)) {
- return $this->grav['language']->translate('NICETIME.NO_DATE_PROVIDED', null, true);
- }
- if ($long_strings) {
- $periods = array("NICETIME.SECOND", "NICETIME.MINUTE", "NICETIME.HOUR", "NICETIME.DAY", "NICETIME.WEEK", "NICETIME.MONTH", "NICETIME.YEAR", "NICETIME.DECADE");
- } else {
- $periods = array("NICETIME.SEC", "NICETIME.MIN", "NICETIME.HR", "NICETIME.DAY", "NICETIME.WK", "NICETIME.MO", "NICETIME.YR", "NICETIME.DEC");
- }
- $lengths = array("60","60","24","7","4.35","12","10");
- $now = time();
- // check if unix timestamp
- if ((string)(int)$date == $date) {
- $unix_date = $date;
- } else {
- $unix_date = strtotime($date);
- }
- // check validity of date
- if (empty($unix_date)) {
- return $this->grav['language']->translate('NICETIME.BAD_DATE', null, true);
- }
- // is it future date or past date
- if ($now > $unix_date) {
- $difference = $now - $unix_date;
- $tense = $this->grav['language']->translate('NICETIME.AGO', null, true);
- } else {
- $difference = $unix_date - $now;
- $tense = $this->grav['language']->translate('NICETIME.FROM_NOW', null, true);
- }
- for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) {
- $difference /= $lengths[$j];
- }
- $difference = round($difference);
- if ($difference != 1) {
- $periods[$j] .= '_PLURAL';
- }
- $periods[$j] = $this->grav['language']->translate($periods[$j], null, true);
- return "$difference $periods[$j] {$tense}";
- }
- public function absoluteUrlFilter($string)
- {
- $url = $this->grav['uri']->base();
- $string = preg_replace('/((?:href|src) *= *[\'"](?!(http|ftp)))/i', "$1$url", $string);
- return $string;
- }
- public function markdownFilter($string)
- {
- $page = $this->grav['page'];
- $defaults = $this->config->get('system.pages.markdown');
- // Initialize the preferred variant of Parsedown
- if ($defaults['extra']) {
- $parsedown = new ParsedownExtra($page, $defaults);
- } else {
- $parsedown = new Parsedown($page, $defaults);
- }
- $string = $parsedown->text($string);
- return $string;
- }
- public function startsWithFilter($haystack, $needle)
- {
- return Utils::startsWith($haystack, $needle);
- }
- public function endsWithFilter($haystack, $needle)
- {
- return Utils::endsWith($haystack, $needle);
- }
- public function definedDefaultFilter($value, $default = null)
- {
- if (isset($value)) {
- return $value;
- } else {
- return $default;
- }
- }
- public function rtrimFilter($value, $chars = null)
- {
- return rtrim($value, $chars);
- }
- public function ltrimFilter($value, $chars = null)
- {
- return ltrim($value, $chars);
- }
- public function translate()
- {
- return $this->grav['language']->translate(func_get_args());
- }
- public function translateArray($key, $index, $lang = null)
- {
- return $this->grav['language']->translateArray($key, $index, $lang);
- }
- /**
- * Repeat given string x times.
- *
- * @param string $input
- * @param int $multiplier
- * @return string
- */
- public function repeatFunc($input, $multiplier)
- {
- return str_repeat($input, $multiplier);
- }
- /**
- * Return URL to the resource.
- *
- * @example {{ url('theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4') }}
- *
- * @param string $input Resource to be located.
- * @param bool $domain True to include domain name.
- * @return string|null Returns url to the resource or null if resource was not found.
- */
- public function urlFunc($input, $domain = false)
- {
- if (!trim((string) $input)) {
- return false;
- }
- if (strpos((string) $input, '://')) {
- /** @var UniformResourceLocator $locator */
- $locator = $this->grav['locator'];
- // Get relative path to the resource (or false if not found).
- $resource = $locator->findResource((string) $input, false);
- } else {
- $resource = (string) $input;
- }
- /** @var Uri $uri */
- $uri = $this->grav['uri'];
- return $resource ? rtrim($uri->rootUrl($domain), '/') . '/' . $resource : null;
- }
- /**
- * Evaluate a string
- *
- * @example {{ evaluate('grav.language.getLanguage') }}
- *
- * @param string $input String to be evaluated
- * @return string Returns the evaluated string
- */
- public function evaluateFunc($input)
- {
- return $this->grav['twig']->processString("{{ $input }}");
- }
- /**
- * Based on Twig_Extension_Debug / twig_var_dump
- * (c) 2011 Fabien Potencier
- *
- * @param \Twig_Environment $env
- * @param $context
- */
- public function dump(\Twig_Environment $env, $context)
- {
- if (!$env->isDebug() || !$this->debugger) {
- return;
- }
- $count = func_num_args();
- if (2 === $count) {
- $data = [];
- foreach ($context as $key => $value) {
- if (is_object($value)) {
- if (method_exists($value, 'toArray')) {
- $data[$key] = $value->toArray();
- } else {
- $data[$key] = "Object (" . get_class($value) . ")";
- }
- } else {
- $data[$key] = $value;
- }
- }
- $this->debugger->addMessage($data, 'debug');
- } else {
- for ($i = 2; $i < $count; $i++) {
- $this->debugger->addMessage(func_get_arg($i), 'debug');
- }
- }
- }
- /**
- * Output a Gist
- *
- * @param string $id
- * @return string
- */
- public function gistFunc($id)
- {
- return '<script src="https://gist.github.com/'.$id.'.js"></script>';
- }
- /**
- * Generate a random string
- *
- * @param int $count
- *
- * @return string
- */
- public function randomStringFunc($count = 5)
- {
- return Utils::generateRandomString($count);
- }
- /**
- * Cast a value to array
- *
- * @param $value
- *
- * @return array
- */
- public function arrayFunc($value)
- {
- return (array) $value;
- }
- /**
- * Returns a string from a value. If the value is array, return it json encoded
- *
- * @param $value
- *
- * @return string
- */
- public function stringFunc($value)
- {
- if (is_array($value)) { //format the array as a string
- return json_encode($value);
- } else {
- return $value;
- }
- }
- /**
- * Translate a string
- *
- * @return string
- */
- public function translateFunc()
- {
- return $this->grav['language']->translate(func_get_args());
- }
- /**
- * Authorize an action. Returns true if the user is logged in and has the right to execute $action.
- *
- * @param string $action
- *
- * @return bool
- */
- public function authorize($action)
- {
- if (!$this->grav['user']->authenticated) {
- return false;
- }
- $action = (array)$action;
- foreach ($action as $a) {
- if ($this->grav['user']->authorize($a)) {
- return true;
- }
- }
- return false;
- }
- }
|