diff --git a/user/plugins/twig-extensions/CHANGELOG.md b/user/plugins/twig-extensions/CHANGELOG.md new file mode 100644 index 0000000..66ed9dd --- /dev/null +++ b/user/plugins/twig-extensions/CHANGELOG.md @@ -0,0 +1,23 @@ +# v1.1.0 +## 12/09/2018 + +1. [](#new) + * Updated to Twig-extensions 1.5.4 + +# v1.0.2 +## 05/14/2017 + +1. [](#new) + * Tweaked `shuffle` to handle associative arrays as well, thanks to @Lamecarlate. + +# v1.0.1 +## 09/30/2016 + +1. [](#new) + * Added demo URL + +# v1.0.0 +## 09/30/2016 + +1. [](#new) + * ChangeLog started... diff --git a/user/plugins/twig-extensions/LICENSE b/user/plugins/twig-extensions/LICENSE new file mode 100644 index 0000000..ff905ac --- /dev/null +++ b/user/plugins/twig-extensions/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Aaron Dalton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/user/plugins/twig-extensions/README.md b/user/plugins/twig-extensions/README.md new file mode 100644 index 0000000..c8b6836 --- /dev/null +++ b/user/plugins/twig-extensions/README.md @@ -0,0 +1,67 @@ +# Twig Extensions Plugin + +The **Twig Extensions** plugin is for [Grav CMS](http://github.com/getgrav/grav). It pulls in a subset of the official [Twig Extensions](https://github.com/twigphp/Twig-extensions), v1.4.0. + +For a demo, [visit my blog](https://perlkonig.com/demos/twig-extensions). + +## Installation + +Installing the Twig Extensions plugin can be done in one of two ways. The GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. + +### GPM Installation (Preferred) + +The simplest way to install this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm) through your system's terminal (also called the command line). From the root of your Grav install type: + + bin/gpm install twig-extensions + +This will install the Twig Extensions plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/twig-extensions`. + +### Manual Installation + +To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `twig-extensions`. You can find these files on [GitHub](https://github.com/Perlkonig/grav-plugin-twig-extensions) or via [GetGrav.org](http://getgrav.org/downloads/plugins#extras). + +You should now have all the plugin files under + + /your/site/grav/user/plugins/twig-extensions + +> NOTE: This plugin is a modular component for Grav which requires [Grav](http://github.com/getgrav/grav) and the [Error](https://github.com/getgrav/grav-plugin-error) and [Problems](https://github.com/getgrav/grav-plugin-problems) to operate. + +## Configuration + +Below is the default configuration. An explanation of the various fields follows. To customize, first copy `twig-extensions.yaml` to your `user/config/plugins` folder and edit that copy. + +``` +enabled: true +modules: [array, intl, date] + +``` + +* The `enabled` field turns the plugin off and on. + +* The `modules` array tells the plugin which modules you want imported. This plugin only imports three of the five modules. These are the only valid options. + +## Usage + +Simply enable the plugin to use these Twig filters. There are three modules available: + +* The `Intl` module provides three filters: + * `localizeddate` formats a date based on the locale. + * `localizednumber` formats a number based on the locale. + * `localizedcurrency` formats a number based on a given currency code. + +* The `Array` module provides a single filter: + * `shuffle` randomizes an array. + * **Note:** This code was slightly modified to allow shuffling associative arrays. Simply pass `true` to enable this feature: `{{ myArray | shuffle(true) }}`. + +The `Date` module also only provides a single filter: + * `time_diff` dispays the delta between two dates in a human readable form (e.g., `2 days ago`). + +For more information, [read the official documentation](http://twig.sensiolabs.org/doc/extensions/index.html). + +### Omitted Modules + +* The `Text` module is omitted because Grav already has `truncate` built in, and the `wordwrap` provided here is not very helpful. + +* The `I18n` module is omitted because Grav already has extensive i18n features. + + diff --git a/user/plugins/twig-extensions/blueprints.yaml b/user/plugins/twig-extensions/blueprints.yaml new file mode 100644 index 0000000..c7dca5e --- /dev/null +++ b/user/plugins/twig-extensions/blueprints.yaml @@ -0,0 +1,27 @@ +name: Twig Extensions +version: 1.1.0 +description: Incorporates a subset of the official [Twig Extensions](https://github.com/twigphp/Twig-extensions) +icon: filter +author: + name: Aaron Dalton + email: aaron@daltons.ca +homepage: https://github.com/Perlkonig/grav-plugin-twig-extensions +keywords: grav, plugin, twig, extensions +bugs: https://github.com/Perlkonig/grav-plugin-twig-extensions/issues +docs: https://github.com/Perlkonig/grav-plugin-twig-extensions/blob/master/README.md +demo: https://perlkonig.com/demos/twig-extensions +license: MIT + +form: + validation: strict + fields: + enabled: + type: toggle + label: Plugin status + highlight: 1 + default: 0 + options: + 1: Enabled + 0: Disabled + validate: + type: bool diff --git a/user/plugins/twig-extensions/twig-extensions.php b/user/plugins/twig-extensions/twig-extensions.php new file mode 100644 index 0000000..a8e0115 --- /dev/null +++ b/user/plugins/twig-extensions/twig-extensions.php @@ -0,0 +1,62 @@ + ['onPluginsInitialized', 0] + ]; + } + + /** + * Initialize the plugin + */ + public function onPluginsInitialized() + { + // Don't proceed if we are in the admin plugin + if ($this->isAdmin()) { + return; + } + + // Enable the main event we are interested in + $this->enable([ + 'onTwigExtensions' => ['onTwigExtensions', -100], + ]); + } + + public function onTwigExtensions() + { + $modules = $this->grav['config']->get('plugins.twig-extensions.modules'); + if (in_array('intl', $modules)) { + require_once(__DIR__ . '/vendor/Twig/Intl.php'); + $this->grav['twig']->twig->addExtension(new \Twig_Extensions_Extension_Intl()); + } + if (in_array('array', $modules)) { + require_once(__DIR__ . '/vendor/Twig/Array.php'); + $this->grav['twig']->twig->addExtension(new \Twig_Extensions_Extension_Array()); + } + if (in_array('date', $modules)) { + require_once(__DIR__ . '/vendor/Twig/Date.php'); + $this->grav['twig']->twig->addExtension(new \Twig_Extensions_Extension_Date()); + } + } +} diff --git a/user/plugins/twig-extensions/twig-extensions.yaml b/user/plugins/twig-extensions/twig-extensions.yaml new file mode 100644 index 0000000..3e44e69 --- /dev/null +++ b/user/plugins/twig-extensions/twig-extensions.yaml @@ -0,0 +1,3 @@ +enabled: true +modules: [array, intl, date] + diff --git a/user/plugins/twig-extensions/vendor/Twig/Array.php b/user/plugins/twig-extensions/vendor/Twig/Array.php new file mode 100644 index 0000000..a5eaf6d --- /dev/null +++ b/user/plugins/twig-extensions/vendor/Twig/Array.php @@ -0,0 +1,56 @@ + + */ +class Twig_Extensions_Extension_Array extends Twig_Extension +{ + /** + * {@inheritdoc} + */ + public function getFilters() + { + $filters = array( + new Twig_SimpleFilter('shuffle', 'twig_shuffle_filter'), + ); + + return $filters; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'array'; + } +} + +/** + * Shuffles an array. + * + * @param array|Traversable $array An array + * + * @return array + */ +function twig_shuffle_filter($array) +{ + if ($array instanceof Traversable) { + $array = iterator_to_array($array, false); + } + + shuffle($array); + + return $array; +} + +class_alias('Twig_Extensions_Extension_Array', 'Twig\Extensions\ArrayExtension', false); diff --git a/user/plugins/twig-extensions/vendor/Twig/Date.php b/user/plugins/twig-extensions/vendor/Twig/Date.php new file mode 100644 index 0000000..1cdd455 --- /dev/null +++ b/user/plugins/twig-extensions/vendor/Twig/Date.php @@ -0,0 +1,107 @@ + + */ +class Twig_Extensions_Extension_Date extends Twig_Extension +{ + public static $units = array( + 'y' => 'year', + 'm' => 'month', + 'd' => 'day', + 'h' => 'hour', + 'i' => 'minute', + 's' => 'second', + ); + + /** + * @var TranslatorInterface + */ + private $translator; + + public function __construct(TranslatorInterface $translator = null) + { + // Ignore the IdentityTranslator, otherwise the parameters won't be replaced properly + if ($translator instanceof IdentityTranslator) { + $translator = null; + } + + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + new Twig_SimpleFilter('time_diff', array($this, 'diff'), array('needs_environment' => true)), + ); + } + + /** + * Filter for converting dates to a time ago string like Facebook and Twitter has. + * + * @param Twig_Environment $env a Twig_Environment instance + * @param string|DateTime $date a string or DateTime object to convert + * @param string|DateTime $now A string or DateTime object to compare with. If none given, the current time will be used. + * + * @return string the converted time + */ + public function diff(Twig_Environment $env, $date, $now = null) + { + // Convert both dates to DateTime instances. + $date = twig_date_converter($env, $date); + $now = twig_date_converter($env, $now); + + // Get the difference between the two DateTime objects. + $diff = $date->diff($now); + + // Check for each interval if it appears in the $diff object. + foreach (self::$units as $attribute => $unit) { + $count = $diff->$attribute; + + if (0 !== $count) { + return $this->getPluralizedInterval($count, $diff->invert, $unit); + } + } + + return ''; + } + + protected function getPluralizedInterval($count, $invert, $unit) + { + if ($this->translator) { + $id = sprintf('diff.%s.%s', $invert ? 'in' : 'ago', $unit); + + return $this->translator->transChoice($id, $count, array('%count%' => $count), 'date'); + } + + if (1 !== $count) { + $unit .= 's'; + } + + return $invert ? "in $count $unit" : "$count $unit ago"; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'date'; + } +} + +class_alias('Twig_Extensions_Extension_Date', 'Twig\Extensions\DateExtension', false); diff --git a/user/plugins/twig-extensions/vendor/Twig/Intl.php b/user/plugins/twig-extensions/vendor/Twig/Intl.php new file mode 100644 index 0000000..49b0f88 --- /dev/null +++ b/user/plugins/twig-extensions/vendor/Twig/Intl.php @@ -0,0 +1,146 @@ + true)), + new Twig_SimpleFilter('localizednumber', 'twig_localized_number_filter'), + new Twig_SimpleFilter('localizedcurrency', 'twig_localized_currency_filter'), + ); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'intl'; + } +} + +function twig_localized_date_filter(Twig_Environment $env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null, $calendar = 'gregorian') +{ + $date = twig_date_converter($env, $date, $timezone); + + $formatValues = array( + 'none' => IntlDateFormatter::NONE, + 'short' => IntlDateFormatter::SHORT, + 'medium' => IntlDateFormatter::MEDIUM, + 'long' => IntlDateFormatter::LONG, + 'full' => IntlDateFormatter::FULL, + ); + + if (PHP_VERSION_ID < 50500 || !class_exists('IntlTimeZone')) { + $formatter = IntlDateFormatter::create( + $locale, + $formatValues[$dateFormat], + $formatValues[$timeFormat], + $date->getTimezone()->getName(), + 'gregorian' === $calendar ? IntlDateFormatter::GREGORIAN : IntlDateFormatter::TRADITIONAL, + $format + ); + + return $formatter->format($date->getTimestamp()); + } + + $formatter = IntlDateFormatter::create( + $locale, + $formatValues[$dateFormat], + $formatValues[$timeFormat], + IntlTimeZone::createTimeZone($date->getTimezone()->getName()), + 'gregorian' === $calendar ? IntlDateFormatter::GREGORIAN : IntlDateFormatter::TRADITIONAL, + $format + ); + + return $formatter->format($date->getTimestamp()); +} + +function twig_localized_number_filter($number, $style = 'decimal', $type = 'default', $locale = null) +{ + static $typeValues = array( + 'default' => NumberFormatter::TYPE_DEFAULT, + 'int32' => NumberFormatter::TYPE_INT32, + 'int64' => NumberFormatter::TYPE_INT64, + 'double' => NumberFormatter::TYPE_DOUBLE, + 'currency' => NumberFormatter::TYPE_CURRENCY, + ); + + $formatter = twig_get_number_formatter($locale, $style); + + if (!isset($typeValues[$type])) { + throw new Twig_Error_Syntax(sprintf('The type "%s" does not exist. Known types are: "%s"', $type, implode('", "', array_keys($typeValues)))); + } + + return $formatter->format($number, $typeValues[$type]); +} + +function twig_localized_currency_filter($number, $currency = null, $locale = null) +{ + $formatter = twig_get_number_formatter($locale, 'currency'); + + return $formatter->formatCurrency($number, $currency); +} + +/** + * Gets a number formatter instance according to given locale and formatter. + * + * @param string $locale Locale in which the number would be formatted + * @param int $style Style of the formatting + * + * @return NumberFormatter A NumberFormatter instance + */ +function twig_get_number_formatter($locale, $style) +{ + static $formatter, $currentStyle; + + $locale = null !== $locale ? $locale : Locale::getDefault(); + + if ($formatter && $formatter->getLocale() === $locale && $currentStyle === $style) { + // Return same instance of NumberFormatter if parameters are the same + // to those in previous call + return $formatter; + } + + static $styleValues = array( + 'decimal' => NumberFormatter::DECIMAL, + 'currency' => NumberFormatter::CURRENCY, + 'percent' => NumberFormatter::PERCENT, + 'scientific' => NumberFormatter::SCIENTIFIC, + 'spellout' => NumberFormatter::SPELLOUT, + 'ordinal' => NumberFormatter::ORDINAL, + 'duration' => NumberFormatter::DURATION, + ); + + if (!isset($styleValues[$style])) { + throw new Twig_Error_Syntax(sprintf('The style "%s" does not exist. Known styles are: "%s"', $style, implode('", "', array_keys($styleValues)))); + } + + $currentStyle = $style; + + $formatter = NumberFormatter::create($locale, $styleValues[$style]); + + return $formatter; +} + +class_alias('Twig_Extensions_Extension_Intl', 'Twig\Extensions\IntlExtension', false); diff --git a/user/plugins/twig-extensions/vendor/Twig/LICENSE b/user/plugins/twig-extensions/vendor/Twig/LICENSE new file mode 100644 index 0000000..b420d71 --- /dev/null +++ b/user/plugins/twig-extensions/vendor/Twig/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2017 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.