From acd02af00b19798ad751a7ae0ecdae744636ccbe Mon Sep 17 00:00:00 2001 From: tbreuss Date: Sun, 4 Dec 2022 16:08:19 +0100 Subject: [PATCH 01/15] wip Signed-off-by: tbreuss --- plugins/twig_plus/TwigPlusExtension.php | 90 +++++------------- templates/pages/filtered.twig | 31 ------ templates/pages/recent.twig | 15 --- tests/acceptance/HerbieInfoCest.php | 2 - .../SysPlugins/TwigPlus/TwigPlusSmokeTest.php | 14 --- website/site/pages-de.zip | Bin 31387 -> 0 bytes website/site/pages/3-recipes/1-index.md | 19 +++- website/site/themes/default/css/styles.css | 2 +- website/site/themes/default/recipe.twig | 22 ++++- 9 files changed, 58 insertions(+), 137 deletions(-) delete mode 100644 templates/pages/filtered.twig delete mode 100644 templates/pages/recent.twig delete mode 100644 website/site/pages-de.zip diff --git a/plugins/twig_plus/TwigPlusExtension.php b/plugins/twig_plus/TwigPlusExtension.php index c09dafec..7ba6e61d 100644 --- a/plugins/twig_plus/TwigPlusExtension.php +++ b/plugins/twig_plus/TwigPlusExtension.php @@ -48,23 +48,21 @@ public function getFunctions(): array { $options = ['is_safe' => ['html']]; return [ - new TwigFunction('menu_ascii_tree', [$this, 'functionAsciiTree'], $options), - new TwigFunction('menu_breadcrumb', [$this, 'functionBreadcrumb'], $options), - new TwigFunction('menu_list', [$this, 'functionListing'], $options), - new TwigFunction('menu_pager', [$this, 'functionPager'], $options), - new TwigFunction('menu_sitemap', [$this, 'functionSitemap'], $options), - new TwigFunction('menu_tree', [$this, 'functionMenu'], $options), - new TwigFunction('page_taxonomies', [$this, 'functionPageTaxonomies'], $options), - new TwigFunction('pages_filtered', [$this, 'functionPagesFiltered'], $options), - new TwigFunction('pages_recent', [$this, 'functionPagesRecent'], $options), - new TwigFunction('taxonomy_archive', [$this, 'functionTaxonomyArchive'], $options), - new TwigFunction('taxonomy_authors', [$this, 'functionTaxonomyAuthors'], $options), - new TwigFunction('taxonomy_categories', [$this, 'functionTaxonomyCategories'], $options), - new TwigFunction('taxonomy_tags', [$this, 'functionTaxonomyTags'], $options) + new TwigFunction('menu_ascii_tree', [$this, 'menuAsciiTree'], $options), + new TwigFunction('menu_breadcrumb', [$this, 'menuBreadcrumb'], $options), + new TwigFunction('menu_list', [$this, 'menuList'], $options), + new TwigFunction('menu_pager', [$this, 'menuPager'], $options), + new TwigFunction('menu_sitemap', [$this, 'menuSitemap'], $options), + new TwigFunction('menu_tree', [$this, 'menuTree'], $options), + new TwigFunction('page_taxonomies', [$this, 'pageTaxonomies'], $options), + new TwigFunction('taxonomy_archive', [$this, 'taxonomyArchive'], $options), + new TwigFunction('taxonomy_authors', [$this, 'taxonomyAuthors'], $options), + new TwigFunction('taxonomy_categories', [$this, 'taxonomyCategories'], $options), + new TwigFunction('taxonomy_tags', [$this, 'taxonomyTags'], $options) ]; } - public function functionAsciiTree( + public function menuAsciiTree( string $route = '', int $maxDepth = -1, bool $showHidden = false @@ -86,7 +84,7 @@ public function functionAsciiTree( /** * @param array{0: string, 1?: string}|string $homeLink */ - public function functionBreadcrumb( + public function menuBreadcrumb( string $delim = '', $homeLink = '', bool $reverse = false @@ -133,7 +131,7 @@ public function functionBreadcrumb( * @throws RuntimeError * @throws SyntaxError */ - public function functionListing( + public function menuList( ?PageList $pageList = null, string $filter = '', string $sort = '', @@ -170,7 +168,7 @@ public function functionListing( return $this->environment->render($template, ['pagination' => $pagination]); } - public function functionMenu( + public function menuTree( string $route = '', int $maxDepth = -1, bool $showHidden = false, @@ -203,7 +201,7 @@ public function functionMenu( * @throws RuntimeError * @throws SyntaxError */ - public function functionPageTaxonomies( + public function pageTaxonomies( ?Page $page = null, string $pageRoute = '', bool $renderAuthors = true, @@ -223,7 +221,7 @@ public function functionPageTaxonomies( /** * @throws \Exception */ - public function functionPager( + public function menuPager( string $limit = '', string $prevPageLabel = '', string $nextPageLabel = '', @@ -287,55 +285,13 @@ public function functionPager( return strtr($template, $replacements); } - /** - * @param array $routeParams - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function functionPagesFiltered( - array $routeParams, - string $template = '@template/pages/filtered.twig' - ): string { - return $this->environment->render($template, [ - 'routeParams' => $routeParams - ]); - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function functionPagesRecent( - ?PageList $pageList = null, - string $dateFormat = '%e. %B %Y', - int $limit = 5, - ?string $pageType = null, - bool $showDate = false, - string $title = 'Recent posts', - string $template = '@template/pages/recent.twig' - ): string { - if ($pageList === null) { - $pageList = $this->pageRepository->findAll(); - } - $recentPages = $pageList->getRecent($limit, $pageType); - return $this->environment->render($template, [ - 'recentPages' => $recentPages, - 'dateFormat' => $dateFormat, - 'pageType' => $pageType, - 'showDate' => $showDate, - 'title' => $title - ]); - } - - public function functionSitemap( + public function menuSitemap( string $route = '', int $maxDepth = -1, bool $showHidden = false, string $class = 'sitemap' ): string { - return $this->functionMenu($route, $maxDepth, $showHidden, $class); + return $this->menuTree($route, $maxDepth, $showHidden, $class); } /** @@ -343,7 +299,7 @@ public function functionSitemap( * @throws RuntimeError * @throws SyntaxError */ - public function functionTaxonomyArchive( + public function taxonomyArchive( ?PageList $pageList = null, string $pageRoute = '', string $pageType = '', @@ -369,7 +325,7 @@ public function functionTaxonomyArchive( * @throws RuntimeError * @throws SyntaxError */ - public function functionTaxonomyAuthors( + public function taxonomyAuthors( ?PageList $pageList = null, string $pageRoute = '', string $pageType = '', @@ -395,7 +351,7 @@ public function functionTaxonomyAuthors( * @throws RuntimeError * @throws SyntaxError */ - public function functionTaxonomyCategories( + public function taxonomyCategories( ?PageList $pageList = null, string $pageRoute = '', string $pageType = '', @@ -421,7 +377,7 @@ public function functionTaxonomyCategories( * @throws RuntimeError * @throws SyntaxError */ - public function functionTaxonomyTags( + public function taxonomyTags( ?PageList $pageList = null, string $pageRoute = '', string $pageType = '', diff --git a/templates/pages/filtered.twig b/templates/pages/filtered.twig deleted file mode 100644 index 8ca8bd60..00000000 --- a/templates/pages/filtered.twig +++ /dev/null @@ -1,31 +0,0 @@ -{% apply spaceless %} - {% if routeParams %} - - {% if routeParams.author %} - -

Filtered by Author "{{ routeParams.author }}"

- - {% elseif routeParams.category %} - -

Filtered by Category "{{ routeParams.category }}"

- - {% elseif routeParams.tag %} - -

Filtered by Tag "{{ routeParams.tag }}"

- - {% elseif routeParams.year and routeParams.month and routeParams.day %} - -

Filtered by Year/Month/Day "{{ routeParams.year }}-{{ routeParams.month }}-{{ routeParams.day }}"

- - {% elseif routeParams.year and routeParams.month %} - -

Filtered by Year/Month "{{ routeParams.year }}-{{ routeParams.month }}"

- - {% elseif routeParams.year %} - -

Filtered by Year "{{ routeParams.year }}"

- - {% endif %} - - {% endif %} -{% endapply %} \ No newline at end of file diff --git a/templates/pages/recent.twig b/templates/pages/recent.twig deleted file mode 100644 index 0fa87f0c..00000000 --- a/templates/pages/recent.twig +++ /dev/null @@ -1,15 +0,0 @@ -{% apply spaceless %} - {% if recentPages|length > 0 %} -
-

{{ title }}

-
    - {% for pageItem in recentPages %} -
  • - {{ page_link(pageItem.route, pageItem.title) }} - {% if showDate %}
    {{ pageItem.date|strftime(dateFormat) }}{% endif %} -
  • - {% endfor %} -
-
- {% endif %} -{% endapply %} \ No newline at end of file diff --git a/tests/acceptance/HerbieInfoCest.php b/tests/acceptance/HerbieInfoCest.php index e5d46136..0d11f11a 100644 --- a/tests/acceptance/HerbieInfoCest.php +++ b/tests/acceptance/HerbieInfoCest.php @@ -442,8 +442,6 @@ public function testNumberAndSortingOfTwigFunctions(AcceptanceTester $I) 'menu_sitemap', 'menu_tree', 'page_taxonomies', - 'pages_filtered', - 'pages_recent', 'taxonomy_archive', 'taxonomy_authors', 'taxonomy_categories', diff --git a/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php b/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php index 261953b0..36f75099 100644 --- a/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php +++ b/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php @@ -69,20 +69,6 @@ public function testMenuPagerFunction(): void $this->assertEquals($expected, $actual); } - public function testPagesFilteredFunction(): void - { - $expected = ''; - $actual = $this->twig()->renderString('{{ pages_filtered([]) }}'); - $this->assertEquals($expected, $actual); - } - - public function testPagesRecentFunction(): void - { - $expected = '

Recent posts

'; - $actual = $this->twig()->renderString('{{ pages_recent(limit=1) }}'); - $this->assertEquals($expected, $actual); - } - public function testMenuSitemapFunction(): void { $expected = ''; diff --git a/website/site/pages-de.zip b/website/site/pages-de.zip deleted file mode 100644 index 8955a7a6e43040562f916ce5a9ff2a44af67957b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31387 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 by Author "{{ routeParams.author }}"

+ {% elseif routeParams.category %} +

Filtered by Category "{{ routeParams.category }}"

+ {% elseif routeParams.tag %} +

Filtered by Tag "{{ routeParams.tag }}"

+ {% elseif routeParams.year and routeParams.month and routeParams.day %} +

Filtered by Year/Month/Day "{{ routeParams.year }}-{{ routeParams.month }}-{{ routeParams.day }}"

+ {% elseif routeParams.year and routeParams.month %} +

Filtered by Year/Month "{{ routeParams.year }}-{{ routeParams.month }}"

+ {% elseif routeParams.year %} +

Filtered by Year "{{ routeParams.year }}"

+ {% endif %} +{% endapply %} -{% set items = site.pageList.filterItems('recipe', 'recipes', routeParams) %} -{% for item in items %} +{% for item in site.pageList.filterItems('recipe', 'recipes', routeParams) %}

{{ page_link(item.route, item.title) }}
{{ item.date|strftime("%e. %B %Y") }}

diff --git a/website/site/themes/default/css/styles.css b/website/site/themes/default/css/styles.css index a2cd44ec..ac8bc05a 100644 --- a/website/site/themes/default/css/styles.css +++ b/website/site/themes/default/css/styles.css @@ -256,7 +256,7 @@ table { font-size:0.8945em; line-height:1.7; } -.sidebar .widget-blog { +.sidebar .widget { padding-bottom:2em; } .sidebar a { diff --git a/website/site/themes/default/recipe.twig b/website/site/themes/default/recipe.twig index c36519bf..416de85a 100644 --- a/website/site/themes/default/recipe.twig +++ b/website/site/themes/default/recipe.twig @@ -13,11 +13,25 @@
From d6cf67bd38d6a1449589d08498984b3e57dbb363 Mon Sep 17 00:00:00 2001 From: tbreuss Date: Tue, 6 Dec 2022 22:45:18 +0100 Subject: [PATCH 02/15] wip Signed-off-by: tbreuss --- plugins/twig/README.md | 19 + .../TwigExtension.php} | 244 +++++++++- .../TwigPlugin.php} | 11 +- plugins/twig/config.php | 10 + plugins/twig_core/README.md | 19 - plugins/twig_core/config.php | 10 - plugins/twig_plus/README.md | 19 - plugins/twig_plus/TwigPlusExtension.php | 422 ------------------ plugins/twig_plus/TwigPlusPlugin.php | 40 -- plugins/twig_plus/config.php | 10 - templates/page/taxonomies.twig | 58 --- templates/taxonomy/archive.twig | 14 - templates/taxonomy/authors.twig | 13 - templates/taxonomy/categories.twig | 13 - templates/taxonomy/tags.twig | 13 - tests/_data/site/config/main.php | 2 +- tests/_data/site/pages/index.html | 2 +- tests/_data/site/pages/plugins/twig-plus.html | 4 - tests/acceptance/HerbieInfoCest.php | 42 +- .../Fixtures/markdown/config/main.php | 2 +- .../integration/Fixtures/site/config/main.php | 2 +- .../Fixtures/textile/config/main.php | 2 +- .../SysPlugins/TwigPlus/TwigPlusSmokeTest.php | 35 -- website/site/config/main.php | 2 +- website/site/data/plugins.yml | 19 +- website/site/themes/default/blog.twig | 26 -- website/site/themes/default/recipe.twig | 7 - 27 files changed, 298 insertions(+), 762 deletions(-) create mode 100644 plugins/twig/README.md rename plugins/{twig_core/TwigCoreExtension.php => twig/TwigExtension.php} (63%) rename plugins/{twig_core/TwigCorePlugin.php => twig/TwigPlugin.php} (77%) create mode 100755 plugins/twig/config.php delete mode 100644 plugins/twig_core/README.md delete mode 100755 plugins/twig_core/config.php delete mode 100644 plugins/twig_plus/README.md delete mode 100644 plugins/twig_plus/TwigPlusExtension.php delete mode 100644 plugins/twig_plus/TwigPlusPlugin.php delete mode 100755 plugins/twig_plus/config.php delete mode 100644 templates/page/taxonomies.twig delete mode 100644 templates/taxonomy/archive.twig delete mode 100644 templates/taxonomy/authors.twig delete mode 100644 templates/taxonomy/categories.twig delete mode 100644 templates/taxonomy/tags.twig delete mode 100644 tests/_data/site/pages/plugins/twig-plus.html delete mode 100644 website/site/themes/default/blog.twig diff --git a/plugins/twig/README.md b/plugins/twig/README.md new file mode 100644 index 00000000..11e1b89f --- /dev/null +++ b/plugins/twig/README.md @@ -0,0 +1,19 @@ +# Twig System Plugin + +`Twig` is a [Herbie](http://github.com/getherbie) system plugin that brings support for several Twig filters, functions and tests. + +## Installation + +The plugin is installed already. + +To activate it, add `twig` to the `enabledSysPlugins` configuration option. + +~~~php +return [ + 'enabledSysPlugins' => 'twig' +]; +~~~ + +## More Information + +For more information, see . diff --git a/plugins/twig_core/TwigCoreExtension.php b/plugins/twig/TwigExtension.php similarity index 63% rename from plugins/twig_core/TwigCoreExtension.php rename to plugins/twig/TwigExtension.php index 76510806..2b84e1bf 100644 --- a/plugins/twig_core/TwigCoreExtension.php +++ b/plugins/twig/TwigExtension.php @@ -2,15 +2,21 @@ declare(strict_types=1); -namespace herbie\sysplugin\twig_core; +namespace herbie\sysplugin\twig; use Ausi\SlugGenerator\SlugGenerator; use herbie\Alias; use herbie\Assets; use herbie\Config; +use herbie\Page; +use herbie\PageList; +use herbie\PageRepositoryInterface; use herbie\PageTree; use herbie\PageTreeFilterIterator; +use herbie\PageTreeHtmlRenderer; use herbie\PageTreeIterator; +use herbie\PageTreeTextRenderer; +use herbie\Pagination; use herbie\Selector; use herbie\Site; use herbie\Translator; @@ -30,18 +36,14 @@ use function herbie\str_trailing_slash; use function herbie\time_format; -final class TwigCoreExtension extends AbstractExtension +final class TwigExtension extends AbstractExtension { private Alias $alias; - private Assets $assets; - private Environment $environment; - + private PageRepositoryInterface $pageRepository; private SlugGenerator $slugGenerator; - private Translator $translator; - private UrlManager $urlManager; /** @@ -51,6 +53,7 @@ public function __construct( Alias $alias, Assets $assets, Environment $environment, + PageRepositoryInterface $pageRepository, SlugGenerator $slugGenerator, Translator $translator, UrlManager $urlManager @@ -58,6 +61,7 @@ public function __construct( $this->alias = $alias; $this->assets = $assets; $this->environment = $environment; + $this->pageRepository = $pageRepository; $this->slugGenerator = $slugGenerator; $this->translator = $translator; $this->urlManager = $urlManager; @@ -92,6 +96,12 @@ public function getFunctions(): array ]), new TwigFunction('file', [$this, 'functionFile'], ['is_safe' => ['html']]), new TwigFunction('image', [$this, 'functionImage'], ['is_safe' => ['html']]), + new TwigFunction('menu_ascii_tree', [$this, 'menuAsciiTree'], ['is_safe' => ['html']]), + new TwigFunction('menu_breadcrumb', [$this, 'menuBreadcrumb'], ['is_safe' => ['html']]), + new TwigFunction('menu_list', [$this, 'menuList'], ['is_safe' => ['html']]), + new TwigFunction('menu_pager', [$this, 'menuPager'], ['is_safe' => ['html']]), + new TwigFunction('menu_sitemap', [$this, 'menuSitemap'], ['is_safe' => ['html']]), + new TwigFunction('menu_tree', [$this, 'menuTree'], ['is_safe' => ['html']]), new TwigFunction('page_link', [$this, 'functionPageLink'], ['is_safe' => ['html']]), new TwigFunction('page_title', [$this, 'functionPageTitle'], ['needs_context' => true]), new TwigFunction('output_css', [$this, 'functionOutputCss'], ['is_safe' => ['html']]), @@ -301,6 +311,216 @@ public function functionMailLink( } } + public function menuAsciiTree( + string $route = '', + int $maxDepth = -1, + bool $showHidden = false + ): string { + // TODO use $class parameter + $branch = $this->pageRepository->findAll()->getPageTree()->findByRoute($route); + if ($branch === null) { + return ''; + } + + $treeIterator = new PageTreeIterator($branch); + $filterIterator = new PageTreeFilterIterator($treeIterator, !$showHidden); + + $asciiTree = new PageTreeTextRenderer($filterIterator); + $asciiTree->setMaxDepth($maxDepth); + return $asciiTree->render(); + } + + /** + * @param array{0: string, 1?: string}|string $homeLink + */ + public function menuBreadcrumb( + string $delim = '', + $homeLink = '', + bool $reverse = false + ): string { + // TODO use string type for param $homeLink (like "route|label") + + $links = []; + + if (!empty($homeLink)) { + if (is_array($homeLink)) { + $route = reset($homeLink); + $label = isset($homeLink[1]) ? $homeLink[1] : 'Home'; + } else { + $route = $homeLink; + $label = 'Home'; + } + $links[] = $this->createLink($route, $label); + } + + [$route] = $this->urlManager->parseRequest(); + $pageTrail = $this->pageRepository->findAll()->getPageTrail($route); + foreach ($pageTrail as $item) { + $links[] = $this->createLink($item->getRoute(), $item->getMenuTitle()); + } + + if (!empty($reverse)) { + $links = array_reverse($links); + } + + $html = ''; + + return $html; + } + + /** + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ + public function menuList( + ?PageList $pageList = null, + string $filter = '', + string $sort = '', + bool $shuffle = false, + int $limit = 10, + string $template = '@snippet/listing.twig' + ): string { + if ($pageList === null) { + $pageList = $this->pageRepository->findAll(); + } + + if (!empty($filter)) { + [$field, $value] = explode('|', $filter); + $pageList = $pageList->filter($field, $value); + } + + if (!empty($sort)) { + [$field, $direction] = explode('|', $sort); + $pageList = $pageList->sort($field, $direction); + } + + if ($shuffle) { + $pageList = $pageList->shuffle(); + } + + // filter pages with empty title + $pageList = $pageList->filter(function (Page $page) { + return !empty($page->getTitle()); + }); + + $pagination = new Pagination($pageList); + $pagination->setLimit($limit); + + return $this->environment->render($template, ['pagination' => $pagination]); + } + + public function menuTree( + string $route = '', + int $maxDepth = -1, + bool $showHidden = false, + string $class = 'menu' + ): string { + // NOTE duplicated code, see function sitemap + $branch = $this->pageRepository->findAll()->getPageTree()->findByRoute($route); + if ($branch === null) { + return ''; + } + + $treeIterator = new PageTreeIterator($branch); + $filterIterator = new PageTreeFilterIterator($treeIterator, !$showHidden); + + $htmlTree = new PageTreeHtmlRenderer($filterIterator); + $htmlTree->setMaxDepth($maxDepth); + $htmlTree->setClass($class); + $htmlTree->setItemCallback(function (PageTree $node) { + $menuItem = $node->getMenuItem(); + $href = $this->urlManager->createUrl($menuItem->getRoute()); + return sprintf('%s', $href, $menuItem->getMenuTitle()); + }); + + [$currenRoute] = $this->urlManager->parseRequest(); + return $htmlTree->render($currenRoute); + } + + /** + * @throws \Exception + */ + public function menuPager( + string $limit = '', + string $prevPageLabel = '', + string $nextPageLabel = '', + string $prevPageIcon = '', + string $nextPageIcon = '', + string $cssClass = 'pager', + string $template = '
{prev}{next}
' + ): string { + [$route] = $this->urlManager->parseRequest(); + $pageList = $this->pageRepository->findAll(); + + if ($limit !== '') { + $pageList = $pageList->filter(function ($page) use ($limit) { + return strpos($page->getRoute(), $limit) === 0; + }); + } + + $prevPage = null; + $currentPage = null; + $nextPage = null; + $lastPage = null; + foreach ($pageList as $key => $page) { + if ($currentPage) { + $nextPage = $page; + break; + } + if ($key === $route) { + $prevPage = $lastPage; + $currentPage = $page; + continue; + } + $lastPage = $page; + } + + $replacements = [ + '{class}' => $cssClass, + '{prev}' => '', + '{next}' => '' + ]; + + if (isset($prevPage)) { + $label = empty($prevPageLabel) ? $prevPage->getMenuTitle() : $prevPageLabel; + $label = sprintf('%s', $cssClass, $label); + if ($prevPageIcon) { + $label = sprintf('%s%s', $cssClass, $prevPageIcon, $label); + } + $attribs = ['class' => $cssClass . '-link-prev']; + $replacements['{prev}'] = $this->createLink($prevPage->getRoute(), $label, $attribs); + } + + if (isset($nextPage)) { + $label = empty($nextPageLabel) ? $nextPage->getMenuTitle() : $nextPageLabel; + $label = sprintf('%s', $cssClass, $label); + if ($nextPageIcon) { + $label = sprintf('%s%s', $label, $cssClass, $nextPageIcon); + } + $attribs = ['class' => $cssClass . '-link-next']; + $replacements['{next}'] = $this->createLink($nextPage->getRoute(), $label, $attribs); + } + + return strtr($template, $replacements); + } + + public function menuSitemap( + string $route = '', + int $maxDepth = -1, + bool $showHidden = false, + string $class = 'sitemap' + ): string { + return $this->menuTree($route, $maxDepth, $showHidden, $class); + } + public function functionOutputCss(?string $group = null, bool $addTimestamp = false): string { return $this->assets->outputCss($group, $addTimestamp); @@ -463,4 +683,14 @@ public function testIsWritable(string $alias): bool $filename = $this->alias->get($alias); return is_writable($filename); } + + /** + * @param array $htmlAttributes + */ + protected function createLink(string $route, string $label, array $htmlAttributes = []): string + { + $url = $this->urlManager->createUrl($route); + $attributesAsString = $this->buildHtmlAttributes($htmlAttributes); + return sprintf('%s', $url, $attributesAsString, $label); + } } diff --git a/plugins/twig_core/TwigCorePlugin.php b/plugins/twig/TwigPlugin.php similarity index 77% rename from plugins/twig_core/TwigCorePlugin.php rename to plugins/twig/TwigPlugin.php index 981c2374..30921a62 100644 --- a/plugins/twig_core/TwigCorePlugin.php +++ b/plugins/twig/TwigPlugin.php @@ -2,20 +2,22 @@ declare(strict_types=1); -namespace herbie\sysplugin\twig_core; +namespace herbie\sysplugin\twig; use Ausi\SlugGenerator\SlugGenerator; use herbie\Alias; use herbie\Assets; use herbie\event\TwigInitializedEvent; +use herbie\PageRepositoryInterface; use herbie\Plugin; use herbie\Translator; use herbie\UrlManager; -final class TwigCorePlugin extends Plugin +final class TwigPlugin extends Plugin { private Alias $alias; private Assets $assets; + private PageRepositoryInterface $pageRepository; private SlugGenerator $slugGenerator; private Translator $translator; private UrlManager $urlManager; @@ -23,12 +25,14 @@ final class TwigCorePlugin extends Plugin public function __construct( Alias $alias, Assets $assets, + PageRepositoryInterface $pageRepository, SlugGenerator $slugGenerator, Translator $translator, UrlManager $urlManager ) { $this->alias = $alias; $this->assets = $assets; + $this->pageRepository = $pageRepository; $this->slugGenerator = $slugGenerator; $this->translator = $translator; $this->urlManager = $urlManager; @@ -43,10 +47,11 @@ public function eventListeners(): array public function onTwigInitialized(TwigInitializedEvent $event): void { - $event->getEnvironment()->addExtension(new TwigCoreExtension( + $event->getEnvironment()->addExtension(new TwigExtension( $this->alias, $this->assets, $event->getEnvironment(), + $this->pageRepository, $this->slugGenerator, $this->translator, $this->urlManager diff --git a/plugins/twig/config.php b/plugins/twig/config.php new file mode 100755 index 00000000..2744e481 --- /dev/null +++ b/plugins/twig/config.php @@ -0,0 +1,10 @@ + 2, + 'pluginName' => 'twig', + 'pluginClass' => TwigPlugin::class, + 'pluginPath' => __DIR__, +]; diff --git a/plugins/twig_core/README.md b/plugins/twig_core/README.md deleted file mode 100644 index 01dc5e70..00000000 --- a/plugins/twig_core/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Twig Core System Plugin - -`Twig Core` is a [Herbie](http://github.com/getherbie) system plugin that brings support for several Twig filters, functions and tests. - -## Installation - -The plugin is installed already. - -To activate it, add `twig_core` to the `enabledSysPlugins` configuration option. - -~~~php -return [ - 'enabledSysPlugins' => 'twig_core' -]; -~~~ - -## More Information - -For more information, see . diff --git a/plugins/twig_core/config.php b/plugins/twig_core/config.php deleted file mode 100755 index 6aefc3d5..00000000 --- a/plugins/twig_core/config.php +++ /dev/null @@ -1,10 +0,0 @@ - 2, - 'pluginName' => 'twig_core', - 'pluginClass' => TwigCorePlugin::class, - 'pluginPath' => __DIR__, -]; diff --git a/plugins/twig_plus/README.md b/plugins/twig_plus/README.md deleted file mode 100644 index 80dedc14..00000000 --- a/plugins/twig_plus/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Twig Plus System Plugin - -`Twig Plus` is a [Herbie](http://github.com/getherbie) system plugin that brings support for several Twig functions. - -## Installation - -The plugin is installed already. - -To activate it, add `twig_plus` to the `enabledSysPlugins` configuration option. - -~~~php -return [ - 'enabledSysPlugins' => 'twig_plus' -]; -~~~ - -## More Information - -For more information, see . diff --git a/plugins/twig_plus/TwigPlusExtension.php b/plugins/twig_plus/TwigPlusExtension.php deleted file mode 100644 index 7ba6e61d..00000000 --- a/plugins/twig_plus/TwigPlusExtension.php +++ /dev/null @@ -1,422 +0,0 @@ -environment = $environment; - $this->pageRepository = $pageRepository; - $this->urlManager = $urlManager; - } - - /** - * @return TwigFunction[] - */ - public function getFunctions(): array - { - $options = ['is_safe' => ['html']]; - return [ - new TwigFunction('menu_ascii_tree', [$this, 'menuAsciiTree'], $options), - new TwigFunction('menu_breadcrumb', [$this, 'menuBreadcrumb'], $options), - new TwigFunction('menu_list', [$this, 'menuList'], $options), - new TwigFunction('menu_pager', [$this, 'menuPager'], $options), - new TwigFunction('menu_sitemap', [$this, 'menuSitemap'], $options), - new TwigFunction('menu_tree', [$this, 'menuTree'], $options), - new TwigFunction('page_taxonomies', [$this, 'pageTaxonomies'], $options), - new TwigFunction('taxonomy_archive', [$this, 'taxonomyArchive'], $options), - new TwigFunction('taxonomy_authors', [$this, 'taxonomyAuthors'], $options), - new TwigFunction('taxonomy_categories', [$this, 'taxonomyCategories'], $options), - new TwigFunction('taxonomy_tags', [$this, 'taxonomyTags'], $options) - ]; - } - - public function menuAsciiTree( - string $route = '', - int $maxDepth = -1, - bool $showHidden = false - ): string { - // TODO use $class parameter - $branch = $this->pageRepository->findAll()->getPageTree()->findByRoute($route); - if ($branch === null) { - return ''; - } - - $treeIterator = new PageTreeIterator($branch); - $filterIterator = new PageTreeFilterIterator($treeIterator, !$showHidden); - - $asciiTree = new PageTreeTextRenderer($filterIterator); - $asciiTree->setMaxDepth($maxDepth); - return $asciiTree->render(); - } - - /** - * @param array{0: string, 1?: string}|string $homeLink - */ - public function menuBreadcrumb( - string $delim = '', - $homeLink = '', - bool $reverse = false - ): string { - // TODO use string type for param $homeLink (like "route|label") - - $links = []; - - if (!empty($homeLink)) { - if (is_array($homeLink)) { - $route = reset($homeLink); - $label = isset($homeLink[1]) ? $homeLink[1] : 'Home'; - } else { - $route = $homeLink; - $label = 'Home'; - } - $links[] = $this->createLink($route, $label); - } - - [$route] = $this->urlManager->parseRequest(); - $pageTrail = $this->pageRepository->findAll()->getPageTrail($route); - foreach ($pageTrail as $item) { - $links[] = $this->createLink($item->getRoute(), $item->getMenuTitle()); - } - - if (!empty($reverse)) { - $links = array_reverse($links); - } - - $html = ''; - - return $html; - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function menuList( - ?PageList $pageList = null, - string $filter = '', - string $sort = '', - bool $shuffle = false, - int $limit = 10, - string $template = '@snippet/listing.twig' - ): string { - if ($pageList === null) { - $pageList = $this->pageRepository->findAll(); - } - - if (!empty($filter)) { - [$field, $value] = explode('|', $filter); - $pageList = $pageList->filter($field, $value); - } - - if (!empty($sort)) { - [$field, $direction] = explode('|', $sort); - $pageList = $pageList->sort($field, $direction); - } - - if ($shuffle) { - $pageList = $pageList->shuffle(); - } - - // filter pages with empty title - $pageList = $pageList->filter(function (Page $page) { - return !empty($page->getTitle()); - }); - - $pagination = new Pagination($pageList); - $pagination->setLimit($limit); - - return $this->environment->render($template, ['pagination' => $pagination]); - } - - public function menuTree( - string $route = '', - int $maxDepth = -1, - bool $showHidden = false, - string $class = 'menu' - ): string { - // NOTE duplicated code, see function sitemap - $branch = $this->pageRepository->findAll()->getPageTree()->findByRoute($route); - if ($branch === null) { - return ''; - } - - $treeIterator = new PageTreeIterator($branch); - $filterIterator = new PageTreeFilterIterator($treeIterator, !$showHidden); - - $htmlTree = new PageTreeHtmlRenderer($filterIterator); - $htmlTree->setMaxDepth($maxDepth); - $htmlTree->setClass($class); - $htmlTree->setItemCallback(function (PageTree $node) { - $menuItem = $node->getMenuItem(); - $href = $this->urlManager->createUrl($menuItem->getRoute()); - return sprintf('%s', $href, $menuItem->getMenuTitle()); - }); - - [$currenRoute] = $this->urlManager->parseRequest(); - return $htmlTree->render($currenRoute); - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function pageTaxonomies( - ?Page $page = null, - string $pageRoute = '', - bool $renderAuthors = true, - bool $renderCategories = true, - bool $renderTags = true, - string $template = '@template/page/taxonomies.twig' - ): string { - return $this->environment->render($template, [ - 'page' => $page, - 'pageRoute' => $pageRoute, - 'renderAuthors' => $renderAuthors, - 'renderCategories' => $renderCategories, - 'renderTags' => $renderTags - ]); - } - - /** - * @throws \Exception - */ - public function menuPager( - string $limit = '', - string $prevPageLabel = '', - string $nextPageLabel = '', - string $prevPageIcon = '', - string $nextPageIcon = '', - string $cssClass = 'pager', - string $template = '
{prev}{next}
' - ): string { - [$route] = $this->urlManager->parseRequest(); - $pageList = $this->pageRepository->findAll(); - - if ($limit !== '') { - $pageList = $pageList->filter(function ($page) use ($limit) { - return strpos($page->getRoute(), $limit) === 0; - }); - } - - $prevPage = null; - $currentPage = null; - $nextPage = null; - $lastPage = null; - foreach ($pageList as $key => $page) { - if ($currentPage) { - $nextPage = $page; - break; - } - if ($key === $route) { - $prevPage = $lastPage; - $currentPage = $page; - continue; - } - $lastPage = $page; - } - - $replacements = [ - '{class}' => $cssClass, - '{prev}' => '', - '{next}' => '' - ]; - - if (isset($prevPage)) { - $label = empty($prevPageLabel) ? $prevPage->getMenuTitle() : $prevPageLabel; - $label = sprintf('%s', $cssClass, $label); - if ($prevPageIcon) { - $label = sprintf('%s%s', $cssClass, $prevPageIcon, $label); - } - $attribs = ['class' => $cssClass . '-link-prev']; - $replacements['{prev}'] = $this->createLink($prevPage->getRoute(), $label, $attribs); - } - - if (isset($nextPage)) { - $label = empty($nextPageLabel) ? $nextPage->getMenuTitle() : $nextPageLabel; - $label = sprintf('%s', $cssClass, $label); - if ($nextPageIcon) { - $label = sprintf('%s%s', $label, $cssClass, $nextPageIcon); - } - $attribs = ['class' => $cssClass . '-link-next']; - $replacements['{next}'] = $this->createLink($nextPage->getRoute(), $label, $attribs); - } - - return strtr($template, $replacements); - } - - public function menuSitemap( - string $route = '', - int $maxDepth = -1, - bool $showHidden = false, - string $class = 'sitemap' - ): string { - return $this->menuTree($route, $maxDepth, $showHidden, $class); - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function taxonomyArchive( - ?PageList $pageList = null, - string $pageRoute = '', - string $pageType = '', - bool $showCount = false, - string $title = 'Archive', - string $template = '@template/taxonomy/archive.twig' - ): string { - if ($pageList === null) { - $pageList = $this->pageRepository->findAll(); - } - $months = $pageList->getMonths($pageType); - return $this->environment->render($template, [ - 'months' => $months, - 'pageRoute' => $pageRoute, - 'pageType' => $pageType, - 'showCount' => $showCount, - 'title' => $title - ]); - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function taxonomyAuthors( - ?PageList $pageList = null, - string $pageRoute = '', - string $pageType = '', - bool $showCount = false, - string $title = 'Authors', - string $template = '@template/taxonomy/authors.twig' - ): string { - if ($pageList === null) { - $pageList = $this->pageRepository->findAll(); - } - $authors = $pageList->getAuthors($pageType); - return $this->environment->render($template, [ - 'authors' => $authors, - 'pageRoute' => $pageRoute, - 'pageType' => $pageType, - 'showCount' => $showCount, - 'title' => $title - ]); - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function taxonomyCategories( - ?PageList $pageList = null, - string $pageRoute = '', - string $pageType = '', - bool $showCount = false, - string $title = 'Categories', - string $template = '@template/taxonomy/categories.twig' - ): string { - if ($pageList === null) { - $pageList = $this->pageRepository->findAll(); - } - $categories = $pageList->getCategories($pageType); - return $this->environment->render($template, [ - 'categories' => $categories, - 'pageRoute' => $pageRoute, - 'pageType' => $pageType, - 'showCount' => $showCount, - 'title' => $title - ]); - } - - /** - * @throws LoaderError - * @throws RuntimeError - * @throws SyntaxError - */ - public function taxonomyTags( - ?PageList $pageList = null, - string $pageRoute = '', - string $pageType = '', - bool $showCount = false, - string $title = 'Tags', - string $template = '@template/taxonomy/tags.twig' - ): string { - if ($pageList === null) { - $pageList = $this->pageRepository->findAll(); - } - $tags = $pageList->getTags($pageType); - return $this->environment->render($template, [ - 'pageRoute' => $pageRoute, - 'pageType' => $pageType, - 'showCount' => $showCount, - 'tags' => $tags, - 'title' => $title - ]); - } - - /** - * @param array $htmlOptions - */ - protected function buildHtmlAttributes(array $htmlOptions = []): string - { - $attributes = ''; - foreach ($htmlOptions as $key => $value) { - $attributes .= $key . '="' . $value . '" '; - } - return trim($attributes); - } - - /** - * @param array $htmlAttributes - */ - protected function createLink(string $route, string $label, array $htmlAttributes = []): string - { - $url = $this->urlManager->createUrl($route); - $attributesAsString = $this->buildHtmlAttributes($htmlAttributes); - return sprintf('%s', $url, $attributesAsString, $label); - } -} diff --git a/plugins/twig_plus/TwigPlusPlugin.php b/plugins/twig_plus/TwigPlusPlugin.php deleted file mode 100644 index 762b465c..00000000 --- a/plugins/twig_plus/TwigPlusPlugin.php +++ /dev/null @@ -1,40 +0,0 @@ -pageRepository = $pageRepository; - $this->urlManager = $urlManager; - } - - public function eventListeners(): array - { - return [ - [TwigInitializedEvent::class, [$this, 'onTwigInitialized']], - ]; - } - - public function onTwigInitialized(TwigInitializedEvent $event): void - { - $event->getEnvironment()->addExtension(new TwigPlusExtension( - $event->getEnvironment(), - $this->pageRepository, - $this->urlManager - )); - } -} diff --git a/plugins/twig_plus/config.php b/plugins/twig_plus/config.php deleted file mode 100755 index 0c2ea3d8..00000000 --- a/plugins/twig_plus/config.php +++ /dev/null @@ -1,10 +0,0 @@ - 2, - 'pluginName' => 'twig_plus', - 'pluginClass' => TwigPlusPlugin::class, - 'pluginPath' => __DIR__, -]; diff --git a/templates/page/taxonomies.twig b/templates/page/taxonomies.twig deleted file mode 100644 index 4b6efc1a..00000000 --- a/templates/page/taxonomies.twig +++ /dev/null @@ -1,58 +0,0 @@ -{% apply spaceless %} - {% if renderCategories and renderTags and renderAuthors %} -
- - {# render markup for categories #} - {% if renderCategories %} - {% set categories = page.getCategories() %} - {% if categories %} - - Categories: - - {% set delim = '' %} - {% for category in categories %} - {%- set route = pageRoute ~ '/category/' ~ category|slugify -%} - {{ delim|raw }}{{ page_link(route, category) }} - {%- set delim = ', ' -%} - {% endfor %} - - {% endif %} - {% endif %} - - {# render markup for tags #} - {% if renderTags %} - {% set tags = page.getTags() %} - {% if tags %} - - Tags: - - {% set delim = '' %} - {% for tag in tags %} - {%- set route = pageRoute ~ '/tag/' ~ tag|slugify -%} - {{ delim|raw }}{{ page_link(route, tag) }} - {%- set delim = ', ' -%} - {% endfor %} - - {% endif %} - {% endif %} - - {# render markup for authors #} - {% if renderAuthors %} - {% set authors = page.getAuthors() %} - {% if authors %} - - Authors: - - {% set delim = '' %} - {% for author in authors %} - {%- set route = pageRoute ~ '/author/' ~ author|slugify -%} - {{ delim|raw }}{{ page_link(route, author) }} - {%- set delim = ', ' -%} - {% endfor %} - - {% endif %} - {% endif %} - -
- {% endif %} -{% endapply %} \ No newline at end of file diff --git a/templates/taxonomy/archive.twig b/templates/taxonomy/archive.twig deleted file mode 100644 index 78a6abfe..00000000 --- a/templates/taxonomy/archive.twig +++ /dev/null @@ -1,14 +0,0 @@ -{% apply spaceless %} - {% if months|length > 0 %} -
-

{{ title }}

-
    - {% for item in months %} - {% set route = pageRoute ~ '/' ~ item.year ~ '/' ~ item.month %} - {% set label = item.date|strftime('%B %Y') %} -
  • {{ page_link(route, label) }}{% if showCount %} ({{ item.count }}){% endif %}
  • - {% endfor %} -
-
- {% endif %} -{% endapply %} \ No newline at end of file diff --git a/templates/taxonomy/authors.twig b/templates/taxonomy/authors.twig deleted file mode 100644 index 58183ad7..00000000 --- a/templates/taxonomy/authors.twig +++ /dev/null @@ -1,13 +0,0 @@ -{% apply spaceless %} - {% if authors|length > 0 %} -
-

{{ title }}

-
    - {% for author, count in authors %} - {% set route = pageRoute ~ '/author/' ~ author|slugify %} -
  • {{ page_link(route, author) }}{% if showCount %} ({{ count }}){% endif %}
  • - {% endfor %} -
-
- {% endif %} -{% endapply %} \ No newline at end of file diff --git a/templates/taxonomy/categories.twig b/templates/taxonomy/categories.twig deleted file mode 100644 index 703b567e..00000000 --- a/templates/taxonomy/categories.twig +++ /dev/null @@ -1,13 +0,0 @@ -{% apply spaceless %} - {% if categories|length > 0 %} -
-

{{ title }}

-
    - {% for category, count in categories %} - {% set route = pageRoute ~ '/category/' ~ category|slugify %} -
  • {{ page_link(route, category) }}{% if showCount %} ({{ count }}){% endif %}
  • - {% endfor %} -
-
- {% endif %} -{% endapply %} \ No newline at end of file diff --git a/templates/taxonomy/tags.twig b/templates/taxonomy/tags.twig deleted file mode 100644 index 08a4a5c5..00000000 --- a/templates/taxonomy/tags.twig +++ /dev/null @@ -1,13 +0,0 @@ -{% apply spaceless %} - {% if tags|length > 0 %} -
-

{{ title }}

-
    - {% for tag, count in tags %} - {% set route = pageRoute ~ '/tag/' ~ tag|slugify %} -
  • {{ page_link(route, tag) }}{% if showCount %} ({{ count }}){% endif %}
  • - {% endfor %} -
-
- {% endif %} -{% endapply %} \ No newline at end of file diff --git a/tests/_data/site/config/main.php b/tests/_data/site/config/main.php index 5ec2dca7..bcad7fc3 100644 --- a/tests/_data/site/config/main.php +++ b/tests/_data/site/config/main.php @@ -18,7 +18,7 @@ ] ], 'enabledPlugins' => '', - 'enabledSysPlugins' => 'twig_core,twig_plus,markdown,rest,textile,imagine,dummy', + 'enabledSysPlugins' => 'twig,markdown,rest,textile,imagine,dummy', 'plugins' => [ 'imagine' => [ 'test' => true, diff --git a/tests/_data/site/pages/index.html b/tests/_data/site/pages/index.html index 832c938e..d14a4eb5 100644 --- a/tests/_data/site/pages/index.html +++ b/tests/_data/site/pages/index.html @@ -6,7 +6,7 @@

Herbie Tests

{{ 100000|filesize }} -{{ sitemap() }} +{{ menu_sitemap() }}

Downloads

diff --git a/tests/_data/site/pages/plugins/twig-plus.html b/tests/_data/site/pages/plugins/twig-plus.html deleted file mode 100644 index 53e1d20a..00000000 --- a/tests/_data/site/pages/plugins/twig-plus.html +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Twig Plus Plugin -hidden: false ---- diff --git a/tests/acceptance/HerbieInfoCest.php b/tests/acceptance/HerbieInfoCest.php index 0d11f11a..0c95c626 100644 --- a/tests/acceptance/HerbieInfoCest.php +++ b/tests/acceptance/HerbieInfoCest.php @@ -107,10 +107,8 @@ public function testNumberAndSortingOfPhpClasses(AcceptanceTester $I) 'herbie\sysplugin\markdown\MarkdownSysPlugin', 'herbie\sysplugin\rest\RestSysPlugin', 'herbie\sysplugin\textile\TextileSysPlugin', - 'herbie\sysplugin\twig_core\TwigCoreExtension', - 'herbie\sysplugin\twig_core\TwigCorePlugin', - 'herbie\sysplugin\twig_plus\TwigPlusExtension', - 'herbie\sysplugin\twig_plus\TwigPlusPlugin', + 'herbie\sysplugin\twig\TwigExtension', + 'herbie\sysplugin\twig\TwigPlugin', ]; $I->amOnPage('/herbie-info'); $I->see('PHP Classes (' . count($classes) . ')', 'h2'); @@ -309,16 +307,11 @@ public function testNumberAndSortingOfConfig(AcceptanceTester $I) 'plugins.textile.pluginClass', 'plugins.textile.pluginName', 'plugins.textile.pluginPath', - 'plugins.twig_core.apiVersion', - 'plugins.twig_core.location', - 'plugins.twig_core.pluginClass', - 'plugins.twig_core.pluginName', - 'plugins.twig_core.pluginPath', - 'plugins.twig_plus.apiVersion', - 'plugins.twig_plus.location', - 'plugins.twig_plus.pluginClass', - 'plugins.twig_plus.pluginName', - 'plugins.twig_plus.pluginPath', + 'plugins.twig.apiVersion', + 'plugins.twig.location', + 'plugins.twig.pluginClass', + 'plugins.twig.pluginName', + 'plugins.twig.pluginPath', 'theme', 'urls.media', 'urls.web', @@ -426,6 +419,12 @@ public function testNumberAndSortingOfTwigFunctions(AcceptanceTester $I) 'file_link', 'file', 'image', + 'menu_ascii_tree', + 'menu_breadcrumb', + 'menu_list', + 'menu_pager', + 'menu_sitemap', + 'menu_tree', 'page_link', 'page_title', 'output_css', @@ -435,17 +434,6 @@ public function testNumberAndSortingOfTwigFunctions(AcceptanceTester $I) 'url', 'abs_url', 'mail_link', - 'menu_ascii_tree', - 'menu_breadcrumb', - 'menu_list', - 'menu_pager', - 'menu_sitemap', - 'menu_tree', - 'page_taxonomies', - 'taxonomy_archive', - 'taxonomy_authors', - 'taxonomy_categories', - 'taxonomy_tags', 'herbie_debug', 'markdown', 'rest', @@ -514,7 +502,6 @@ public function testNumberAndSortingOfEvents(AcceptanceTester $I) 'herbie\event\TwigInitializedEvent', 'herbie\event\TwigInitializedEvent', 'herbie\event\TwigInitializedEvent', - 'herbie\event\TwigInitializedEvent', 'herbie\event\RenderLayoutEvent', 'herbie\event\RenderLayoutEvent', 'herbie\event\RenderLayoutEvent', @@ -542,8 +529,7 @@ public function testNumberAndSortingOfPlugins(AcceptanceTester $I) { $plugins = [ 'CORE', - 'twig_core', - 'twig_plus', + 'twig', 'markdown', 'rest', 'textile', diff --git a/tests/integration/Fixtures/markdown/config/main.php b/tests/integration/Fixtures/markdown/config/main.php index 3b9c9a25..66b6fc95 100644 --- a/tests/integration/Fixtures/markdown/config/main.php +++ b/tests/integration/Fixtures/markdown/config/main.php @@ -8,7 +8,7 @@ 'debug' => true, ], ], - 'enabledSysPlugins' => 'twig_core,twig_plus,markdown', + 'enabledSysPlugins' => 'twig,markdown', 'plugins' => [ 'markdown' => [ 'enableTwigFilter' => false, diff --git a/tests/integration/Fixtures/site/config/main.php b/tests/integration/Fixtures/site/config/main.php index 79711259..3bc25558 100644 --- a/tests/integration/Fixtures/site/config/main.php +++ b/tests/integration/Fixtures/site/config/main.php @@ -9,5 +9,5 @@ 'cache' => false, ], ], - 'enabledSysPlugins' => 'twig_core,twig_plus,dummy,imagine,markdown,textile', + 'enabledSysPlugins' => 'twig,dummy,imagine,markdown,textile', ]; diff --git a/tests/integration/Fixtures/textile/config/main.php b/tests/integration/Fixtures/textile/config/main.php index 233499d0..d88fe5ab 100644 --- a/tests/integration/Fixtures/textile/config/main.php +++ b/tests/integration/Fixtures/textile/config/main.php @@ -1,7 +1,7 @@ 'twig_core,twig_plus,textile', + 'enabledSysPlugins' => 'twig,textile', 'components' => [ 'fileLogger' => null, ], diff --git a/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php b/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php index 36f75099..839b1144 100644 --- a/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php +++ b/tests/integration/SysPlugins/TwigPlus/TwigPlusSmokeTest.php @@ -53,13 +53,6 @@ public function testMenuTreeFunction(): void $this->assertEquals($expected, $actual); } - public function testPageTaxonomiesFunction(): void - { - $expected = '
'; - $actual = $this->twig()->renderString('{{ page_taxonomies() }}'); - $this->assertEquals($expected, $actual); - } - public function testMenuPagerFunction(): void { $expected = '
' @@ -75,32 +68,4 @@ public function testMenuSitemapFunction(): void $actual = $this->twig()->renderString('{{ menu_sitemap(maxDepth=0) }}'); $this->assertEquals($expected, $actual); } - - public function testTaxonomyArchiveFunction(): void - { - $expected = ''; - $actual = $this->twig()->renderString('{{ taxonomy_archive() }}'); - $this->assertEquals($expected, $actual); - } - - public function testTaxonomyAuthorsFunction(): void - { - $expected = ''; - $actual = $this->twig()->renderString('{{ taxonomy_authors() }}'); - $this->assertEquals($expected, $actual); - } - - public function testTaxonomyCategoriesFunction(): void - { - $expected = ''; - $actual = $this->twig()->renderString('{{ taxonomy_categories() }}'); - $this->assertEquals($expected, $actual); - } - - public function testTaxonomyTagsFunction(): void - { - $expected = ''; - $actual = $this->twig()->renderString('{{ taxonomy_tags() }}'); - $this->assertEquals($expected, $actual); - } } diff --git a/website/site/config/main.php b/website/site/config/main.php index 725778fb..83f79ef7 100644 --- a/website/site/config/main.php +++ b/website/site/config/main.php @@ -10,7 +10,7 @@ 'layouts' => 'twig' ], 'enabledPlugins' => 'simplesearch,simplecontact', - 'enabledSysPlugins' => 'twig_core,twig_plus,imagine,markdown', + 'enabledSysPlugins' => 'twig,imagine,markdown', 'components' => [ 'dataRepository' => [ 'adapter' => 'yaml' diff --git a/website/site/data/plugins.yml b/website/site/data/plugins.yml index 6cabeebb..be014f20 100644 --- a/website/site/data/plugins.yml +++ b/website/site/data/plugins.yml @@ -265,27 +265,16 @@ status: stable enabled: false - - id: twig_core - name: Twig Core - desc: This system plugin provides basic Twig filters, functions and tests that are useful for creating a web project with Herbie. + id: twig + name: Twig + desc: This system plugin provides several Twig filters, functions and tests that are useful for creating a web project with Herbie. author: Herbie-Team version: 2.0.0 composer: - github: https://github.com/getherbie/herbie/tree/2.x/plugins/twig_core + github: https://github.com/getherbie/herbie/tree/2.x/plugins/twig packagist: status: stable enabled: true -- - id: twig_plus - name: Twig Plus - desc: This system plugin provides extended Twig functions that are useful for creating a web project with Herbie. - author: Herbie-Team - version: 2.0.0 - composer: - github: https://github.com/getherbie/herbie/tree/2.x/plugins/twig_plus - packagist: - status: stable - enabled: true - id: video name: Video diff --git a/website/site/themes/default/blog.twig b/website/site/themes/default/blog.twig deleted file mode 100644 index b82e3f79..00000000 --- a/website/site/themes/default/blog.twig +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "main.twig" %} - -{% block content %} -
-
-
-
-
- {{ content.default|raw }} - {{ page_taxonomies(page, pageRoute='blog') }} -
-
-
- -
-
-
-
-{% endblock %} diff --git a/website/site/themes/default/recipe.twig b/website/site/themes/default/recipe.twig index 416de85a..443361b3 100644 --- a/website/site/themes/default/recipe.twig +++ b/website/site/themes/default/recipe.twig @@ -7,13 +7,11 @@
{{ content.default|raw }} - {# page_taxonomies(page, pageRoute='recipes') #}
From dd6f51a6ddcfe03873d5fb1d8432d570c9f8f540 Mon Sep 17 00:00:00 2001 From: tbreuss Date: Wed, 7 Dec 2022 21:04:02 +0100 Subject: [PATCH 03/15] wip Signed-off-by: tbreuss --- plugins/twig/TwigExtension.php | 73 ++++++++-------- templates/macros/page.twig | 6 +- templates/macros/pages.twig | 4 +- templates/macros/taxonomy.twig | 10 +-- .../{mail_link.twig => link_mail.twig} | 0 templates/snippets/listing.twig | 8 +- tests/_data/site/pages/index.html | 22 ++--- tests/_data/site/pages/plugins/dummy.html | 2 +- tests/_data/site/themes/default/main.twig | 2 +- tests/acceptance/HerbieInfoCest.php | 26 +++--- .../TwigCore/Filters/FilesizeFilterTest.php | 32 +++---- .../TwigCore/Filters/StrftimeFilterTest.php | 22 ++--- .../TwigCore/Functions/AbsUrlFunctionTest.php | 10 +-- .../TwigCore/Functions/AddCssFunctionTest.php | 2 +- .../TwigCore/Functions/AddJsFunctionTest.php | 2 +- .../Functions/FileLinkFunctionTest.php | 2 +- .../Functions/MailLinkFunctionTest.php | 12 +-- .../Functions/OutputCssFunctionTest.php | 4 +- .../Functions/OutputJsFunctionTest.php | 4 +- .../Functions/PageLinkFunctionTest.php | 2 +- .../TwigCore/Functions/UrlFunctionTest.php | 8 +- .../TwigCore/Tests/ReadableTest.php | 8 +- .../TwigCore/Tests/WritableTest.php | 8 +- website/site/data/twig_filters.yml | 4 +- website/site/data/twig_functions.yml | 83 +++++++++++++++---- website/site/data/twig_tests.yml | 8 +- website/site/pages/3-recipes/1-index.md | 4 +- .../adding-a-sitemap-for-search-engines.md | 4 +- website/site/pages/atom.xml | 10 +-- website/site/pages/imprint.md | 2 +- website/site/pages/robots.txt | 2 +- website/site/pages/sitemap.xml | 2 +- website/site/snippets/twig_features.twig | 2 +- .../themes/default/includes/simplesearch.twig | 2 +- website/site/themes/default/main.twig | 34 ++++---- website/site/themes/default/recipe.twig | 2 +- 36 files changed, 237 insertions(+), 191 deletions(-) rename templates/snippets/{mail_link.twig => link_mail.twig} (100%) diff --git a/plugins/twig/TwigExtension.php b/plugins/twig/TwigExtension.php index 2b84e1bf..b6cb84f9 100644 --- a/plugins/twig/TwigExtension.php +++ b/plugins/twig/TwigExtension.php @@ -73,10 +73,10 @@ public function __construct( public function getFilters(): array { return [ - new TwigFilter('filesize', [$this, 'filterFilesize']), new TwigFilter('find', [$this, 'filterFind'], ['is_variadic' => true]), + new TwigFilter('format_date', [$this, 'filterStrftime']), + new TwigFilter('format_size', [$this, 'filterFilesize']), new TwigFilter('slugify', [$this, 'filterSlugify']), - new TwigFilter('strftime', [$this, 'filterStrftime']), new TwigFilter('visible', [$this, 'filterVisible'], ['deprecated' => true]) // doesn't work properly ]; } @@ -87,30 +87,27 @@ public function getFilters(): array public function getFunctions(): array { return [ - new TwigFunction('add_css', [$this, 'functionAddCss']), - new TwigFunction('add_js', [$this, 'functionAddJs']), - new TwigFunction('css_classes', [$this, 'functionCssClasses'], ['needs_context' => true]), - new TwigFunction('file_link', [$this, 'functionFileLink'], [ - 'is_safe' => ['html'], - 'needs_context' => true - ]), - new TwigFunction('file', [$this, 'functionFile'], ['is_safe' => ['html']]), - new TwigFunction('image', [$this, 'functionImage'], ['is_safe' => ['html']]), + new TwigFunction('css_add', [$this, 'cssAdd']), + new TwigFunction('css_classes', [$this, 'cssClasses'], ['needs_context' => true]), + new TwigFunction('css_out', [$this, 'cssOut'], ['is_safe' => ['html']]), + new TwigFunction('file', [$this, 'file'], ['is_safe' => ['html']]), + new TwigFunction('image', [$this, 'image'], ['is_safe' => ['html']]), + new TwigFunction('js_add', [$this, 'jsAdd']), + new TwigFunction('js_out', [$this, 'jsOut'], ['is_safe' => ['html']]), + new TwigFunction('link_file', [$this, 'linkFile'], ['is_safe' => ['html'], 'needs_context' => true]), + new TwigFunction('link_mail', [$this, 'linkMail'], ['is_safe' => ['html']]), + new TwigFunction('link_page', [$this, 'linkPage'], ['is_safe' => ['html']]), new TwigFunction('menu_ascii_tree', [$this, 'menuAsciiTree'], ['is_safe' => ['html']]), new TwigFunction('menu_breadcrumb', [$this, 'menuBreadcrumb'], ['is_safe' => ['html']]), new TwigFunction('menu_list', [$this, 'menuList'], ['is_safe' => ['html']]), new TwigFunction('menu_pager', [$this, 'menuPager'], ['is_safe' => ['html']]), new TwigFunction('menu_sitemap', [$this, 'menuSitemap'], ['is_safe' => ['html']]), new TwigFunction('menu_tree', [$this, 'menuTree'], ['is_safe' => ['html']]), - new TwigFunction('page_link', [$this, 'functionPageLink'], ['is_safe' => ['html']]), - new TwigFunction('page_title', [$this, 'functionPageTitle'], ['needs_context' => true]), - new TwigFunction('output_css', [$this, 'functionOutputCss'], ['is_safe' => ['html']]), - new TwigFunction('output_js', [$this, 'functionOutputJs'], ['is_safe' => ['html']]), - new TwigFunction('snippet', [$this, 'functionSnippet'], ['is_safe' => ['all']]), - new TwigFunction('translate', [$this, 'functionTranslate']), - new TwigFunction('url', [$this, 'functionUrl']), - new TwigFunction('abs_url', [$this, 'functionAbsUrl']), - new TwigFunction('mail_link', [$this, 'functionMailLink'], ['is_safe' => ['html']]) + new TwigFunction('page_title', [$this, 'pageTitle'], ['needs_context' => true]), + new TwigFunction('snippet', [$this, 'snippet'], ['is_safe' => ['all']]), + new TwigFunction('translate', [$this, 'translate']), + new TwigFunction('url_rel', [$this, 'urlRelative']), + new TwigFunction('url_abs', [$this, 'urlAbsolute']), ]; } @@ -120,8 +117,8 @@ public function getFunctions(): array public function getTests(): array { return [ - new TwigTest('readable', [$this, 'testIsReadable']), - new TwigTest('writable', [$this, 'testIsWritable']) + new TwigTest('file_readable', [$this, 'testIsReadable']), + new TwigTest('file_writable', [$this, 'testIsWritable']) ]; } @@ -202,7 +199,7 @@ public function filterVisible(PageTree $tree): PageTreeFilterIterator /** * @param array|string $paths */ - public function functionAddCss( + public function cssAdd( $paths, array $attr = [], ?string $group = null, @@ -215,7 +212,7 @@ public function functionAddCss( /** * @param array|string $paths */ - public function functionAddJs( + public function jsAdd( $paths, array $attr = [], ?string $group = null, @@ -229,7 +226,7 @@ public function functionAddJs( * @param array $context * @return string */ - public function functionCssClasses(array $context): string + public function cssClasses(array $context): string { $page = 'error'; if (isset($context['page'])) { @@ -256,7 +253,7 @@ public function functionCssClasses(array $context): string return str_replace(['/', '.'], '-', $class); } - public function functionFileLink( + public function linkFile( array $context, string $path, string $label = '', @@ -288,11 +285,11 @@ public function functionFileLink( return strtr('{label}{info}', $replace); } - public function functionMailLink( + public function linkMail( string $email, ?string $label = null, array $attribs = [], - string $template = '@snippet/mail_link.twig' + string $template = '@snippet/link_mail.twig' ): string { $attribs['href'] = 'mailto:' . $email; $attribs['class'] = $attribs['class'] ?? 'link__label'; @@ -521,12 +518,12 @@ public function menuSitemap( return $this->menuTree($route, $maxDepth, $showHidden, $class); } - public function functionOutputCss(?string $group = null, bool $addTimestamp = false): string + public function cssOut(?string $group = null, bool $addTimestamp = false): string { return $this->assets->outputCss($group, $addTimestamp); } - public function functionOutputJs(?string $group = null, bool $addTimestamp = false): string + public function jsOut(?string $group = null, bool $addTimestamp = false): string { return $this->assets->outputJs($group, $addTimestamp); } @@ -537,12 +534,12 @@ public function functionOutputJs(?string $group = null, bool $addTimestamp = fal * @throws RuntimeError * @throws SyntaxError */ - public function functionSnippet(string $path, array $context = []): string + public function snippet(string $path, array $context = []): string { return $this->environment->render($path, $context); } - public function functionImage( + public function image( string $src, int $width = 0, int $height = 0, @@ -564,7 +561,7 @@ public function functionImage( return sprintf('', $this->buildHtmlAttributes($attribs)); } - public function functionPageLink(string $route, string $label, array $attribs = []): string + public function linkPage(string $route, string $label, array $attribs = []): string { $scheme = parse_url($route, PHP_URL_SCHEME); if ($scheme === null) { @@ -592,7 +589,7 @@ public function functionPageLink(string $route, string $label, array $attribs = /** * @param array{site: Site} $context */ - public function functionPageTitle( + public function pageTitle( array $context, string $delim = ' / ', string $siteTitle = '', @@ -622,22 +619,22 @@ public function functionPageTitle( return implode($delim, $titles); } - public function functionTranslate(string $category = '', string $message = '', array $params = []): string + public function translate(string $category = '', string $message = '', array $params = []): string { return $this->translator->translate($category, $message, $params); } - public function functionUrl(string $route = ''): string + public function urlRelative(string $route = ''): string { return $this->urlManager->createUrl($route); } - public function functionAbsUrl(string $route = ''): string + public function urlAbsolute(string $route = ''): string { return $this->urlManager->createAbsoluteUrl($route); } - public function functionFile(string $path, string $label = '', bool $info = false, array $attribs = []): string + public function file(string $path, string $label = '', bool $info = false, array $attribs = []): string { $attribs['class'] = $attribs['class'] ?? 'link__label'; diff --git a/templates/macros/page.twig b/templates/macros/page.twig index bbca12fa..113df59e 100644 --- a/templates/macros/page.twig +++ b/templates/macros/page.twig @@ -58,7 +58,7 @@ {% set delim = '' %} {% for category in categories %} {%- set route = options.pageRoute ~ '/category/' ~ category|slugify -%} - {{ delim|raw }}{{ page_link(route, category) }} + {{ delim|raw }}{{ link_page(route, category) }} {%- set delim = ', ' -%} {% endfor %} @@ -75,7 +75,7 @@ {% set delim = '' %} {% for tag in tags %} {%- set route = options.pageRoute ~ '/tag/' ~ tag|slugify -%} - {{ delim|raw }}{{ page_link(route, tag) }} + {{ delim|raw }}{{ link_page(route, tag) }} {%- set delim = ', ' -%} {% endfor %} @@ -92,7 +92,7 @@ {% set delim = '' %} {% for author in authors %} {%- set route = options.pageRoute ~ '/author/' ~ author|slugify -%} - {{ delim|raw }}{{ page_link(route, author) }} + {{ delim|raw }}{{ link_page(route, author) }} {%- set delim = ', ' -%} {% endfor %} diff --git a/templates/macros/pages.twig b/templates/macros/pages.twig index 0d93a02b..ecf7298d 100644 --- a/templates/macros/pages.twig +++ b/templates/macros/pages.twig @@ -22,8 +22,8 @@
    {% for pageItem in recentPages %}
  • - {{ page_link(pageItem.route, pageItem.title) }} - {% if options.showDate %}
    {{ pageItem.date|strftime(options.dateFormat) }}{% endif %} + {{ link_page(pageItem.route, pageItem.title) }} + {% if options.showDate %}
    {{ pageItem.date|format_date(options.dateFormat) }}{% endif %}
  • {% endfor %}
diff --git a/templates/macros/taxonomy.twig b/templates/macros/taxonomy.twig index f4e903f0..0f99e04b 100644 --- a/templates/macros/taxonomy.twig +++ b/templates/macros/taxonomy.twig @@ -17,8 +17,8 @@
    {% for item in months %} {% set route = pageRoute ~ '/' ~ item.year ~ '/' ~ item.month %} - {% set label = item.date|strftime('%B %Y') %} -
  • {{ page_link(route, label) }}{% if showCount %} ({{ item.count }}){% endif %}
  • + {% set label = item.date|format_date('%B %Y') %} +
  • {{ link_page(route, label) }}{% if showCount %} ({{ item.count }}){% endif %}
  • {% endfor %}
@@ -47,7 +47,7 @@
    {% for author, count in authors %} {% set route = pageRoute ~ '/author/' ~ author|slugify %} -
  • {{ page_link(route, author) }}{% if showCount %} ({{ count }}){% endif %}
  • +
  • {{ link_page(route, author) }}{% if showCount %} ({{ count }}){% endif %}
  • {% endfor %}
@@ -76,7 +76,7 @@
    {% for category, count in categories %} {% set route = pageRoute ~ '/category/' ~ category|slugify %} -
  • {{ page_link(route, category) }}{% if showCount %} ({{ count }}){% endif %}
  • +
  • {{ link_page(route, category) }}{% if showCount %} ({{ count }}){% endif %}
  • {% endfor %}
@@ -105,7 +105,7 @@
    {% for tag, count in tags %} {% set route = pageRoute ~ '/tag/' ~ tag|slugify %} -
  • {{ page_link(route, tag) }}{% if showCount %} ({{ count }}){% endif %}
  • +
  • {{ link_page(route, tag) }}{% if showCount %} ({{ count }}){% endif %}
  • {% endfor %}
diff --git a/templates/snippets/mail_link.twig b/templates/snippets/link_mail.twig similarity index 100% rename from templates/snippets/mail_link.twig rename to templates/snippets/link_mail.twig diff --git a/templates/snippets/listing.twig b/templates/snippets/listing.twig index 30df443e..5c36b4f2 100644 --- a/templates/snippets/listing.twig +++ b/templates/snippets/listing.twig @@ -3,10 +3,10 @@
{% for menuitem in pagination %}
-

{{ page_link(menuitem.route, menuitem.getMenuTitle()) }}

+

{{ link_page(menuitem.route, menuitem.getMenuTitle()) }}

{% if menuitem.image %}
- +
{% endif %}

{{ menuitem.excerpt }}

@@ -17,10 +17,10 @@
diff --git a/tests/_data/site/pages/index.html b/tests/_data/site/pages/index.html index d14a4eb5..94e41200 100644 --- a/tests/_data/site/pages/index.html +++ b/tests/_data/site/pages/index.html @@ -4,21 +4,21 @@

Herbie Tests

-{{ 100000|filesize }} +{{ 100000|format_size }} {{ menu_sitemap() }}

Downloads

-{{ file_link('favicon.ico', 'File', true) }} +{{ link_file('favicon.ico', 'File', true) }} -Download +Download
    -
  • {{ file_link('dummy.pdf') }}
  • -
  • {{ file_link('dummy.pdf', 'Dummy', false) }}
  • -
  • {{ file_link('dummy.pdf', 'Dummy', true) }}
  • -
  • {{ file_link('dummy.pdf', 'Dummy', true, { target:"_blank"}) }}
  • +
  • {{ link_file('dummy.pdf') }}
  • +
  • {{ link_file('dummy.pdf', 'Dummy', false) }}
  • +
  • {{ link_file('dummy.pdf', 'Dummy', true) }}
  • +
  • {{ link_file('dummy.pdf', 'Dummy', true, { target:"_blank"}) }}

Imagine

@@ -36,13 +36,13 @@

Imagine

Links

    -
  • {{ page_link('adfsafsdf', 'About') }}
  • -
  • {{ page_link('about', 'About') }}
  • -
  • {{ page_link('https://example.com/', 'About') }}
  • +
  • {{ link_page('adfsafsdf', 'About') }}
  • +
  • {{ link_page('about', 'About') }}
  • +
  • {{ link_page('https://example.com/', 'About') }}

Mailto-Links

    -
  • {{ mail_link('me@example.com', 'John Doe') }}
  • +
  • {{ link_mail('me@example.com', 'John Doe') }}
diff --git a/tests/_data/site/pages/plugins/dummy.html b/tests/_data/site/pages/plugins/dummy.html index 22dd0130..1cc9e53f 100644 --- a/tests/_data/site/pages/plugins/dummy.html +++ b/tests/_data/site/pages/plugins/dummy.html @@ -9,4 +9,4 @@

Dummy Plugin

{{ dummy("This is from") }}.

{% if "dummy" is dummy %}This is from Dummy Test.{% endif %}

{% apply dummy_dynamic %}This is from {% endapply %}.

-

{{ file_link('dummy.pdf', 'Dummy', true) }}

+

{{ link_file('dummy.pdf', 'Dummy', true) }}

diff --git a/tests/_data/site/themes/default/main.twig b/tests/_data/site/themes/default/main.twig index 166581fa..5d88bcd9 100644 --- a/tests/_data/site/themes/default/main.twig +++ b/tests/_data/site/themes/default/main.twig @@ -19,7 +19,7 @@ Herbie Tests {%- for menu in site.pageTrail -%} {{- ' / ' -}} - {{ menu.title }} + {{ menu.title }} {%- endfor -%} diff --git a/tests/acceptance/HerbieInfoCest.php b/tests/acceptance/HerbieInfoCest.php index 0c95c626..d26b994b 100644 --- a/tests/acceptance/HerbieInfoCest.php +++ b/tests/acceptance/HerbieInfoCest.php @@ -379,10 +379,10 @@ public function testNumberAndSortingOfTwigFilters(AcceptanceTester $I) 'escape', 'e', 'raw', - 'filesize', 'find', + 'format_date', + 'format_size', 'slugify', - 'strftime', 'visible', 'markdown', 'rest', @@ -413,27 +413,27 @@ public function testNumberAndSortingOfTwigFunctions(AcceptanceTester $I) 'include', 'source', 'dump', - 'add_css', - 'add_js', + 'css_add', 'css_classes', - 'file_link', + 'css_out', 'file', 'image', + 'js_add', + 'js_out', + 'link_file', + 'link_mail', + 'link_page', 'menu_ascii_tree', 'menu_breadcrumb', 'menu_list', 'menu_pager', 'menu_sitemap', 'menu_tree', - 'page_link', 'page_title', - 'output_css', - 'output_js', 'snippet', 'translate', - 'url', - 'abs_url', - 'mail_link', + 'url_rel', + 'url_abs', 'herbie_debug', 'markdown', 'rest', @@ -462,8 +462,8 @@ public function testNumberAndSortingOfTwigTests(AcceptanceTester $I) 'constant', 'empty', 'iterable', - 'readable', - 'writable', + 'file_readable', + 'file_writable', 'dummy', 'local_odd', 'mytest', diff --git a/tests/integration/SysPlugins/TwigCore/Filters/FilesizeFilterTest.php b/tests/integration/SysPlugins/TwigCore/Filters/FilesizeFilterTest.php index dca18ef3..55b408db 100644 --- a/tests/integration/SysPlugins/TwigCore/Filters/FilesizeFilterTest.php +++ b/tests/integration/SysPlugins/TwigCore/Filters/FilesizeFilterTest.php @@ -29,33 +29,33 @@ protected function _setUp(): void public function testFilesizeWithWrongType(): void { $this->expectException(TypeError::class); - $twig = '{{ "string"|filesize }}'; // string + $twig = '{{ "string"|format_size }}'; // string $this->twigRenderer->renderString($twig); - $twig = '{{ {a:1}|filesize }}'; // object + $twig = '{{ {a:1}|format_size }}'; // object $this->twigRenderer->renderString($twig); } public function testFilesizeWithNegativeValue(): void { - $twig = '{{ "-1"|filesize }}'; + $twig = '{{ "-1"|format_size }}'; $actual = $this->twigRenderer->renderString($twig); $this->assertSame('0', $actual); } public function testFilesizeWithValidValues(): void { - $this->assertSame('0', $this->twigRenderer->renderString('{{ "0"|filesize }}')); - $this->assertSame('1 Byte', $this->twigRenderer->renderString('{{ "1"|filesize }}')); - $this->assertSame('2 B', $this->twigRenderer->renderString('{{ "2"|filesize }}')); - $this->assertSame('1024 B', $this->twigRenderer->renderString('{{ "1024"|filesize }}')); - $this->assertSame('1 KB', $this->twigRenderer->renderString('{{ "1025"|filesize }}')); // KB (* 1024) - $this->assertSame('1024 KB', $this->twigRenderer->renderString('{{ "1048576"|filesize }}')); - $this->assertSame('1 MB', $this->twigRenderer->renderString('{{ "1048577"|filesize }}')); // MB (* 1024) - $this->assertSame('1024 MB', $this->twigRenderer->renderString('{{ "1073741824"|filesize }}')); - $this->assertSame('1 GB', $this->twigRenderer->renderString('{{ "1073741825"|filesize }}')); // GB (* 1024) - $this->assertSame('1024 GB', $this->twigRenderer->renderString('{{ "1099511627776"|filesize }}')); - $this->assertSame('1 TB', $this->twigRenderer->renderString('{{ "1099511627777"|filesize }}')); // TB (* 1024) - $this->assertSame('1024 TB', $this->twigRenderer->renderString('{{ "1125899906842624"|filesize }}')); - $this->assertSame('1 PB', $this->twigRenderer->renderString('{{ "1125899906842625"|filesize }}')); // TB (* 1024) + $this->assertSame('0', $this->twigRenderer->renderString('{{ "0"|format_size }}')); + $this->assertSame('1 Byte', $this->twigRenderer->renderString('{{ "1"|format_size }}')); + $this->assertSame('2 B', $this->twigRenderer->renderString('{{ "2"|format_size }}')); + $this->assertSame('1024 B', $this->twigRenderer->renderString('{{ "1024"|format_size }}')); + $this->assertSame('1 KB', $this->twigRenderer->renderString('{{ "1025"|format_size }}')); // KB (* 1024) + $this->assertSame('1024 KB', $this->twigRenderer->renderString('{{ "1048576"|format_size }}')); + $this->assertSame('1 MB', $this->twigRenderer->renderString('{{ "1048577"|format_size }}')); // MB (* 1024) + $this->assertSame('1024 MB', $this->twigRenderer->renderString('{{ "1073741824"|format_size }}')); + $this->assertSame('1 GB', $this->twigRenderer->renderString('{{ "1073741825"|format_size }}')); // GB (* 1024) + $this->assertSame('1024 GB', $this->twigRenderer->renderString('{{ "1099511627776"|format_size }}')); + $this->assertSame('1 TB', $this->twigRenderer->renderString('{{ "1099511627777"|format_size }}')); // TB (* 1024) + $this->assertSame('1024 TB', $this->twigRenderer->renderString('{{ "1125899906842624"|format_size }}')); + $this->assertSame('1 PB', $this->twigRenderer->renderString('{{ "1125899906842625"|format_size }}')); // TB (* 1024) } } diff --git a/tests/integration/SysPlugins/TwigCore/Filters/StrftimeFilterTest.php b/tests/integration/SysPlugins/TwigCore/Filters/StrftimeFilterTest.php index 2f6d6770..23388c10 100644 --- a/tests/integration/SysPlugins/TwigCore/Filters/StrftimeFilterTest.php +++ b/tests/integration/SysPlugins/TwigCore/Filters/StrftimeFilterTest.php @@ -33,31 +33,31 @@ public function testStrftimeWithValidDates(): void // timestamp as integer $this->assertSame( '12. September 2022', - $this->twigRenderer->renderString('{{ 1662952416|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ 1662952416|format_date("%e. %B %Y") }}') ); // timestamp as string $this->assertSame( '12. September 2022', - $this->twigRenderer->renderString('{{ "1662952416"|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ "1662952416"|format_date("%e. %B %Y") }}') ); // iso-date $this->assertSame( '12. September 2022', - $this->twigRenderer->renderString('{{ "2022-09-12"|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ "2022-09-12"|format_date("%e. %B %Y") }}') ); // empty string $this->assertSame( time_format("%e. %B %Y"), - $this->twigRenderer->renderString('{{ ""|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ ""|format_date("%e. %B %Y") }}') ); // year with month (without day) $this->assertSame( time_format(" 1. September 2022"), - $this->twigRenderer->renderString('{{ "2022-09"|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ "2022-09"|format_date("%e. %B %Y") }}') ); } @@ -65,32 +65,32 @@ public function testStrftimeWithWrongDates(): void { $this->assertSame( 'invalid-date', - $this->twigRenderer->renderString('{{ "invalid-date"|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ "invalid-date"|format_date("%e. %B %Y") }}') ); $this->assertSame( time_format('%e. %B %Y', time_from_string('1970-01-01')), - $this->twigRenderer->renderString('{{ 2000|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ 2000|format_date("%e. %B %Y") }}') ); $this->assertSame( '2000-13-32', - $this->twigRenderer->renderString('{{ "2000-13-32"|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ "2000-13-32"|format_date("%e. %B %Y") }}') ); $this->assertSame( time_format('%e. %B %Y', time_from_string('2000-12-31')), // quite random - $this->twigRenderer->renderString('{{ "20000-12-31"|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ "20000-12-31"|format_date("%e. %B %Y") }}') ); $this->assertSame( time_format("%e. %B %Y"), - $this->twigRenderer->renderString('{{ false|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ false|format_date("%e. %B %Y") }}') ); $this->assertSame( time_format('%e. %B %Y', time_from_string('1970-01-01')), - $this->twigRenderer->renderString('{{ true|strftime("%e. %B %Y") }}') + $this->twigRenderer->renderString('{{ true|format_date("%e. %B %Y") }}') ); } } diff --git a/tests/integration/SysPlugins/TwigCore/Functions/AbsUrlFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/AbsUrlFunctionTest.php index fe55aa1b..99da485b 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/AbsUrlFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/AbsUrlFunctionTest.php @@ -25,8 +25,8 @@ private function twig(): TwigRenderer public function testUrlWithoutRoute(): void { $tests = [ - ['http:/', '{{ abs_url() }}'], - ['http:/', '{{ abs_url("") }}'], + ['http:/', '{{ url_abs() }}'], + ['http:/', '{{ url_abs("") }}'], ]; foreach ($tests as $test) { $this->assertSame($test[0], $this->twig()->renderString($test[1])); @@ -36,9 +36,9 @@ public function testUrlWithoutRoute(): void public function testUrlWithValidRoutes(): void { $tests = [ - ['http:/one', '{{ abs_url("one") }}'], - ['http:/one/two', '{{ abs_url("one/two") }}'], - ['http:/one/two/three', '{{ abs_url("one/two/three") }}'] + ['http:/one', '{{ url_abs("one") }}'], + ['http:/one/two', '{{ url_abs("one/two") }}'], + ['http:/one/two/three', '{{ url_abs("one/two/three") }}'] ]; foreach ($tests as $test) { $this->assertSame($test[0], $this->twig()->renderString($test[1])); diff --git a/tests/integration/SysPlugins/TwigCore/Functions/AddCssFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/AddCssFunctionTest.php index ef8e83ca..12368b92 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/AddCssFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/AddCssFunctionTest.php @@ -21,7 +21,7 @@ private function twig(): TwigRenderer public function testPowerOn(): void { - $this->twig()->renderString('{{ add_css("@site/assets/styles.css") }}'); + $this->twig()->renderString('{{ css_add("@site/assets/styles.css") }}'); } // TODO write more tests diff --git a/tests/integration/SysPlugins/TwigCore/Functions/AddJsFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/AddJsFunctionTest.php index 2ca9c3f9..950fb53a 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/AddJsFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/AddJsFunctionTest.php @@ -21,7 +21,7 @@ private function twig(): TwigRenderer public function testPowerOn(): void { - $this->twig()->renderString('{{ add_js("@site/assets/script.js") }}'); + $this->twig()->renderString('{{ js_add("@site/assets/script.js") }}'); } // TODO write more tests diff --git a/tests/integration/SysPlugins/TwigCore/Functions/FileLinkFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/FileLinkFunctionTest.php index cd1ffea1..ba193a7a 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/FileLinkFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/FileLinkFunctionTest.php @@ -23,7 +23,7 @@ public function testPowerOn(): void { $this->assertEquals( 'dummy.pdf', - $this->twig()->renderString('{{ file_link("dummy.pdf") }}') + $this->twig()->renderString('{{ link_file("dummy.pdf") }}') ); } diff --git a/tests/integration/SysPlugins/TwigCore/Functions/MailLinkFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/MailLinkFunctionTest.php index c31eddf3..47c32b92 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/MailLinkFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/MailLinkFunctionTest.php @@ -23,7 +23,7 @@ private function twig(): TwigRenderer public function testLinkWithoutParams(): void { $this->expectException(ArgumentCountError::class); - $twig = '{{ mail_link() }}'; + $twig = '{{ link_mail() }}'; $this->twig()->renderString($twig); } @@ -32,7 +32,7 @@ public function testLinkWithEmail(): void $expected = $this->getHtml( 'me@example.com' ); - $twig = '{{ mail_link("me@example.com") }}'; + $twig = '{{ link_mail("me@example.com") }}'; $actual = $this->twig()->renderString($twig); $this->assertSame($expected, $actual); } @@ -42,7 +42,7 @@ public function testLinkWithEmailAndLabel(): void $expected = $this->getHtml( 'Example' ); - $twig = '{{ mail_link("me@example.com", "Example") }}'; + $twig = '{{ link_mail("me@example.com", "Example") }}'; $actual = $this->twig()->renderString($twig); $this->assertSame($expected, $actual); } @@ -52,7 +52,7 @@ public function testLinkWithEmailLabelAndAttribs(): void $expected = $this->getHtml( 'Example' ); - $twig = '{{ mail_link("me@example.com", "Example", {class:"link-class", id:"link-id"}) }}'; + $twig = '{{ link_mail("me@example.com", "Example", {class:"link-class", id:"link-id"}) }}'; $actual = $this->twig()->renderString($twig); $this->assertSame($expected, $actual); } @@ -60,14 +60,14 @@ public function testLinkWithEmailLabelAndAttribs(): void public function testLinkWithCustomTemplate(): void { $expected = 'me@example.com'; - $twig = '{{ mail_link("me@example.com", template="{{label}}") }}'; + $twig = '{{ link_mail("me@example.com", template="{{label}}") }}'; $actual = $this->twig()->renderString($twig); $this->assertSame($expected, $actual); } public function testLinkWithNotExistingCustomTemplate(): void { - $twig = '{{ mail_link("me@example.com", template="@not/existing/template.twig") }}'; + $twig = '{{ link_mail("me@example.com", template="@not/existing/template.twig") }}'; $this->assertEquals('me@example.com', $this->twig()->renderString($twig)); } diff --git a/tests/integration/SysPlugins/TwigCore/Functions/OutputCssFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/OutputCssFunctionTest.php index 55e69820..4a5f3f94 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/OutputCssFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/OutputCssFunctionTest.php @@ -21,8 +21,8 @@ private function twig(): TwigRenderer public function testPowerOn(): void { - $twig = '{{ add_css("@site/assets/styles.css") }}' - . '{{ output_css() }}'; + $twig = '{{ css_add("@site/assets/styles.css") }}' + . '{{ css_out() }}'; $this->assertEquals( '', $this->twig()->renderString($twig) diff --git a/tests/integration/SysPlugins/TwigCore/Functions/OutputJsFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/OutputJsFunctionTest.php index da2becde..e8861c03 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/OutputJsFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/OutputJsFunctionTest.php @@ -21,8 +21,8 @@ private function twig(): TwigRenderer public function testPowerOn(): void { - $twig = '{{ add_js("@site/assets/script.js") }}' - . '{{ output_js() }}'; + $twig = '{{ js_add("@site/assets/script.js") }}' + . '{{ js_out() }}'; $this->assertEquals( '', $this->twig()->renderString($twig) diff --git a/tests/integration/SysPlugins/TwigCore/Functions/PageLinkFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/PageLinkFunctionTest.php index 0e87d8a8..8774b80b 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/PageLinkFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/PageLinkFunctionTest.php @@ -23,7 +23,7 @@ public function testPowerOn(): void { $this->assertEquals( 'label', - $this->twig()->renderString('{{ page_link("route", "label") }}') + $this->twig()->renderString('{{ link_page("route", "label") }}') ); } diff --git a/tests/integration/SysPlugins/TwigCore/Functions/UrlFunctionTest.php b/tests/integration/SysPlugins/TwigCore/Functions/UrlFunctionTest.php index 7d1f55d3..803825bd 100644 --- a/tests/integration/SysPlugins/TwigCore/Functions/UrlFunctionTest.php +++ b/tests/integration/SysPlugins/TwigCore/Functions/UrlFunctionTest.php @@ -22,14 +22,14 @@ private function twig(): TwigRenderer public function testUrlWithoutRoute(): void { - $twig = '{{ url() }}'; + $twig = '{{ url_rel() }}'; $this->assertSame('/', $this->twig()->renderString($twig)); } public function testUrlWithValidRoutes(): void { - $this->assertSame('/one', $this->twig()->renderString('{{ url("one") }}')); - $this->assertSame('/one/two', $this->twig()->renderString('{{ url("one/two") }}')); - $this->assertSame('/one/two/three', $this->twig()->renderString('{{ url("one/two/three") }}')); + $this->assertSame('/one', $this->twig()->renderString('{{ url_rel("one") }}')); + $this->assertSame('/one/two', $this->twig()->renderString('{{ url_rel("one/two") }}')); + $this->assertSame('/one/two/three', $this->twig()->renderString('{{ url_rel("one/two/three") }}')); } } diff --git a/tests/integration/SysPlugins/TwigCore/Tests/ReadableTest.php b/tests/integration/SysPlugins/TwigCore/Tests/ReadableTest.php index 5821c7c4..f6148d9b 100644 --- a/tests/integration/SysPlugins/TwigCore/Tests/ReadableTest.php +++ b/tests/integration/SysPlugins/TwigCore/Tests/ReadableTest.php @@ -42,7 +42,7 @@ public function testReadableWithoutCondition(): void public function testReadableWithEmptyParam(): void { $twig = <<assertSame('not readable', $this->twig()->renderString($twig)); } @@ -50,7 +50,7 @@ public function testReadableWithEmptyParam(): void public function testReadableWithExistingAlias(): void { $twig = <<assertSame('is readable', $this->twig()->renderString($twig)); } @@ -58,7 +58,7 @@ public function testReadableWithExistingAlias(): void public function testReadableWithExistingAliasWithoutPermissions(): void { $twig = <<assertSame('not readable', $this->twig()->renderString($twig)); } @@ -66,7 +66,7 @@ public function testReadableWithExistingAliasWithoutPermissions(): void public function testReadableWithNotExistingAlias(): void { $twig = <<assertSame('not readable', $this->twig()->renderString($twig)); } diff --git a/tests/integration/SysPlugins/TwigCore/Tests/WritableTest.php b/tests/integration/SysPlugins/TwigCore/Tests/WritableTest.php index cd450f9c..ffbd7694 100644 --- a/tests/integration/SysPlugins/TwigCore/Tests/WritableTest.php +++ b/tests/integration/SysPlugins/TwigCore/Tests/WritableTest.php @@ -40,7 +40,7 @@ public function testWritableWithoutCondition(): void public function testWritableWithEmptyParam(): void { $twig = <<assertSame('no', $this->twig()->renderString($twig)); } @@ -48,7 +48,7 @@ public function testWritableWithEmptyParam(): void public function testWritableWithExistingAlias(): void { $twig = <<assertSame('yes', $this->twig()->renderString($twig)); } @@ -56,7 +56,7 @@ public function testWritableWithExistingAlias(): void public function testWritableWithExistingAliasWithoutPermissions(): void { $twig = <<assertSame('no', $this->twig()->renderString($twig)); } @@ -64,7 +64,7 @@ public function testWritableWithExistingAliasWithoutPermissions(): void public function testReadableWithNotExistingAlias(): void { $twig = <<assertSame('no', $this->twig()->renderString($twig)); } diff --git a/website/site/data/twig_filters.yml b/website/site/data/twig_filters.yml index e5ecaac2..1acef209 100644 --- a/website/site/data/twig_filters.yml +++ b/website/site/data/twig_filters.yml @@ -1,7 +1,7 @@ --- - name: filesize code: >- - {{ 12345678|filesize }} + {{ 12345678|format_size }} desc: Returns file size in a human readable format (B, KB, MB, GB, TB, PB). return: - string @@ -45,7 +45,7 @@ - The slugified string - name: strftime code: >- - {{ "2022-09-13"|strftime("%Y-%m-%d") }} + {{ "2022-09-13"|format_date("%Y-%m-%d") }} desc: Formats a local time/date according to locale settings. This is a wrapper for the same-named PHP function. params: diff --git a/website/site/data/twig_functions.yml b/website/site/data/twig_functions.yml index 989c36fb..f829f242 100644 --- a/website/site/data/twig_functions.yml +++ b/website/site/data/twig_functions.yml @@ -1,7 +1,7 @@ --- -- name: abs_url +- name: url_abs code: >- - {{ abs_url("doc/contents/variables") }} + {{ url_abs("doc/contents/variables") }} desc: Returns the absolute URL for a given route. params: - name: route @@ -11,9 +11,9 @@ return: - string - The absolute URL. -- name: add_css +- name: css_add code: >- - {{ add_css("@site/themes/styles.css", {class: "styles"}, "default", false, 1) }} + {{ css_add("@site/themes/styles.css", {class: "styles"}, "default", false, 1) }} desc: Adds a CSS asset to the HTML page. params: - name: paths @@ -38,9 +38,9 @@ default: "-1" return: - void -- name: add_js +- name: js_add code: >- - {{ add_js("@site/themes/script.js", {class: "scripts"}, "default", false, 1) }} + {{ js_add("@site/themes/script.js", {class: "scripts"}, "default", false, 1) }} desc: Adds a JavaScript asset to the HTML page. params: - name: paths @@ -294,9 +294,9 @@ return: - string - The menu as unordered list -- name: output_css +- name: css_out code: >- - {{ output_css("default") }} + {{ css_out("default") }} desc: Outputs the CSS assets for a given group. params: - name: group @@ -310,9 +310,9 @@ return: - void - The CSS assets -- name: output_js +- name: js_out code: >- - {{ output_js("default") }} + {{ js_out("default") }} desc: Outputs the JavaScript assets for a given group. params: - name: group @@ -326,9 +326,57 @@ return: - string - The JavaScript assets -- name: page_link +- name: link_file code: >- - {{ page_link("doc/contents/variables", "Content variables", {class: "page-link"}) }} + {{ link_file("dummy.pdf", "My dummy file", true, {class: "link-file"}) }} + desc: Returns a HTML link with a file from site/media folder. + params: + - name: path + type: string + desc: The path to the file in site/media folder. + default: '' + - name: label + type: string + desc: The label. + default: '' + - name: info + type: bool + desc: Adds info with file size und file extension. + default: false + - name: attribs + type: array + desc: The HTML attributes passed as an associative array. + default: '' + return: + - string + - The link to a file +- name: link_mail + code: >- + {{ link_mail("me@example.com", "John Doe", {class: "link-page"}, "@snippet/link.twig") }} + desc: Returns a HTML mailto link. + params: + - name: email + type: string + desc: The email. + default: '' + - name: label + type: string + desc: The label. + default: '' + - name: attribs + type: array + desc: The HTML attributes passed as an associative array. + default: '' + - name: template + type: string + desc: The aliased path to the template file. + default: '' + return: + - string + - The mailto link +- name: link_page + code: >- + {{ link_page("doc/contents/variables", "Content variables", {class: "link-page"}) }} desc: Returns a HTML link with label and attributes for a given route or an URL. params: - name: route @@ -392,9 +440,10 @@ - string - The included snippet - name: translate - code: >- - {{ translate("app", "Plugin \"{plugin}\" enabled but not found!", {plugin: "dummy"}) }} - desc: Translates a string according to the configuration and locale settings. + code: > + {{ translate("app", "Plugin {plugin} enabled but not found!", {plugin: "dummy"}) }} + desc: > + Translates a string according to the configuration and locale settings. params: - name: category type: string @@ -411,9 +460,9 @@ return: - string - The translated message -- name: url +- name: url_rel code: >- - {{ url("doc/contents") }} + {{ url_rel("doc/contents") }} desc: Returns the relative URL to a given route. params: - name: route diff --git a/website/site/data/twig_tests.yml b/website/site/data/twig_tests.yml index 5ad8ab9f..ffe9fd3f 100644 --- a/website/site/data/twig_tests.yml +++ b/website/site/data/twig_tests.yml @@ -1,15 +1,15 @@ --- -- name: readable +- name: file_readable code: >- - {% if "@site/data" is readable %} + {% if "@site/data" is file_readable %} Path or file is readable {% endif %} desc: Tests if the given alias or path exists and is readable. return: - bool -- name: writable +- name: file_writable code: >- - {% if "@site/data" is writable %} + {% if "@site/data" is file_writable %} Path or file is writable {% endif %} desc: Tests if the given alias or path exists and is writable. diff --git a/website/site/pages/3-recipes/1-index.md b/website/site/pages/3-recipes/1-index.md index 0342ec67..40665d5e 100644 --- a/website/site/pages/3-recipes/1-index.md +++ b/website/site/pages/3-recipes/1-index.md @@ -22,8 +22,8 @@ layout: recipe {% endapply %} {% for item in site.pageList.filterItems('recipe', 'recipes', routeParams) %} -

{{ page_link(item.route, item.title) }}
- {{ item.date|strftime("%e. %B %Y") }} +

{{ link_page(item.route, item.title) }}
+ {{ item.date|format_date("%e. %B %Y") }}

{% else %}

There are no entries available.

diff --git a/website/site/pages/3-recipes/adding-a-sitemap-for-search-engines.md b/website/site/pages/3-recipes/adding-a-sitemap-for-search-engines.md index aab38ebf..aeb563ac 100644 --- a/website/site/pages/3-recipes/adding-a-sitemap-for-search-engines.md +++ b/website/site/pages/3-recipes/adding-a-sitemap-for-search-engines.md @@ -56,7 +56,7 @@ Instead we retrieve a page list and iterate over all pages and output their URL {% for page in site.pageList %} - {{ abs_url(page.route) }} + {{ url_abs(page.route) }} {% if(item.date) %} {{ page.date|date("c") }} {% endif %} @@ -103,7 +103,7 @@ So, create a new page `robots.txt` in the `site/pages` folder with the following --- User-agent: * Allow: / - Sitemap: {{ abs_url('sitemap.xml') }} + Sitemap: {{ url_abs('sitemap.xml') }} {% endverbatim %} In this case, we allow all robots on all paths. diff --git a/website/site/pages/atom.xml b/website/site/pages/atom.xml index fec64efb..56865a57 100644 --- a/website/site/pages/atom.xml +++ b/website/site/pages/atom.xml @@ -8,16 +8,16 @@ hidden: true Herbie CMS Feed Create small but fully functional websites or blogs in no time and with little effort 👌 - - - {{ abs_url('/') }} + + + {{ url_abs('/') }} {{ "now"|date('r') }} {% for item in site.pageList %} {% if item.title != '' %} {{ item.title }} / Herbie CMS - - {{ abs_url(item.route) }} + + {{ url_abs(item.route) }} {% if item.date %}{{ item.date|date("c") }}{% endif %} {% if item.date %}{{ item.date|date("c") }}{% endif %} {% if item.abstract %}{{ item.abstract }}{% endif %} diff --git a/website/site/pages/imprint.md b/website/site/pages/imprint.md index d8be5e75..bc2a1a61 100644 --- a/website/site/pages/imprint.md +++ b/website/site/pages/imprint.md @@ -11,7 +11,7 @@ Birsweg 5
CH-4203 Grellingen
Phone: +41 79 332 67 81
-Email: {{ page_link("contact", "Use the contact form") }} +Email: {{ link_page("contact", "Use the contact form") }} ## Disclaimer diff --git a/website/site/pages/robots.txt b/website/site/pages/robots.txt index 4623ce56..fffff1bc 100644 --- a/website/site/pages/robots.txt +++ b/website/site/pages/robots.txt @@ -4,4 +4,4 @@ layout: "" content_type: text/plain keep_extension: true --- -Sitemap: {{ abs_url('sitemap.xml') }} +Sitemap: {{ url_abs('sitemap.xml') }} diff --git a/website/site/pages/sitemap.xml b/website/site/pages/sitemap.xml index 818e8d17..b6c8956f 100644 --- a/website/site/pages/sitemap.xml +++ b/website/site/pages/sitemap.xml @@ -8,7 +8,7 @@ keep_extension: true {% for item in site.pageList %} - {{ abs_url(item.route) }} + {{ url_abs(item.route) }} {% if(item.date) %} {{ item.date|date("c") }} {% endif %} diff --git a/website/site/snippets/twig_features.twig b/website/site/snippets/twig_features.twig index 492aac85..7b589a26 100644 --- a/website/site/snippets/twig_features.twig +++ b/website/site/snippets/twig_features.twig @@ -1,4 +1,4 @@ -{% for item in attribute(site.data, type) %} +{% for item in attribute(site.data, type)|sort((a, b) => a.name <=> b.name) %}

{{ attribute(item, 'name') }}

diff --git a/website/site/themes/default/includes/simplesearch.twig b/website/site/themes/default/includes/simplesearch.twig index dc84e080..7ff9edf5 100644 --- a/website/site/themes/default/includes/simplesearch.twig +++ b/website/site/themes/default/includes/simplesearch.twig @@ -1,5 +1,5 @@