From 8cb37d38bae33cc4bd278dbd73c150d345ee3d5c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 11 Mar 2019 23:02:02 +0100 Subject: [PATCH 01/73] composer.json: Add 3.0.x-dev alias for pico-3.0 branch --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c3d5dc4..cf7564a 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.0.x-dev", + "dev-pico-3.0": "3.0.x-dev" } } } From 34ae8e88122404d18b433c8b58ecaaa07b9f0c56 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 11 Mar 2019 23:02:45 +0100 Subject: [PATCH 02/73] Support %config.*% Markdown placeholders --- lib/Pico.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Pico.php b/lib/Pico.php index 8cbc4b1..b5d2c2a 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -1495,6 +1495,13 @@ class Pico } } + // replace %config.*% + foreach ($this->config as $configKey => $configValue) { + if (is_scalar($configValue) || ($configValue === null)) { + $variables['%config.' . $configKey . '%'] = (string) $configValue; + } + } + return str_replace(array_keys($variables), $variables, $markdown); } From 787344a5264bba1f859ef95c36f8c3aeed31ff6c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 11 Mar 2019 23:02:57 +0100 Subject: [PATCH 03/73] Rename prev_page Twig variable to previous_page --- lib/Pico.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index b5d2c2a..ff06409 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2009,7 +2009,7 @@ class Pico 'meta' => $this->meta, 'content' => $this->content, 'pages' => $this->pages, - 'prev_page' => $this->previousPage, + 'previous_page' => $this->previousPage, 'current_page' => $this->currentPage, 'next_page' => $this->nextPage, 'version' => static::VERSION From c91518a7c8e898588855f1555347132ad5d56114 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 11 Mar 2019 23:28:09 +0100 Subject: [PATCH 04/73] composer.json: Update Parsedown Extra 0.8 and Parsedown 1.8 version constraints --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index cf7564a..1cb9141 100644 --- a/composer.json +++ b/composer.json @@ -30,8 +30,8 @@ "ext-dom": "*", "twig/twig": "^1.35", "symfony/yaml" : "^2.8", - "erusev/parsedown": "^1.8|1.8.0@beta", - "erusev/parsedown-extra": "^0.8|0.8.0@beta" + "erusev/parsedown": "~1.8.0|1.8.0@beta", + "erusev/parsedown-extra": "~0.8.0|0.8.0@beta" }, "suggest": { "picocms/pico-theme": "Pico requires a theme to actually display the contents of your website. This is Pico's official default theme.", From 008ca6f41b0a0ee20fdd05b9927cad3aea2023fa Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 28 Mar 2019 19:14:23 +0100 Subject: [PATCH 05/73] Pico::loadConfig(): Make twig cache path absolute --- lib/Pico.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index ff06409..c79b5d5 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -948,6 +948,10 @@ class Pico $this->config['twig_config'] = $defaultTwigConfig; } else { $this->config['twig_config'] += $defaultTwigConfig; + + if ($this->config['twig_config']['cache']) { + $this->config['twig_config']['cache'] = $this->getAbsolutePath($this->config['twig_config']['cache']); + } } if (!$this->config['content_dir']) { @@ -2439,21 +2443,28 @@ class Pico * * This method also guarantees a trailing slash. * - * @param string $path relative or absolute path + * @param string $path relative or absolute path + * @param string $basePath treat relative paths relative to the given path; + * defaults to Pico::$rootDir * * @return string absolute path */ - public function getAbsolutePath($path) + public function getAbsolutePath($path, $basePath = null) { + if ($basePath === null) { + $basePath = $this->getRootDir(); + } + if (DIRECTORY_SEPARATOR === '\\') { if (preg_match('/^(?>[a-zA-Z]:\\\\|\\\\\\\\)/', $path) !== 1) { - $path = $this->getRootDir() . $path; + $path = $basePath . $path; } } else { if ($path[0] !== '/') { - $path = $this->getRootDir() . $path; + $path = $basePath . $path; } } + return rtrim($path, '/\\') . '/'; } From edf849725d3b2afaf544283d814fcc840596a82d Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 28 Mar 2019 20:11:46 +0100 Subject: [PATCH 06/73] Config template: Add more Twig config options --- config/config.yml.template | 14 ++++++++------ lib/Pico.php | 9 ++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/config/config.yml.template b/config/config.yml.template index 6a6ac19..4d25dcf 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -2,7 +2,7 @@ # Basic # site_title: Pico # The title of your website -base_url: ~ # Pico will try to guess its base URL, if this fails, override it here +base_url: ~ # Pico will try to guess its base URL, if this fails, override it here; # Example: http://example.com/pico/ rewrite_url: ~ # A boolean (true or false) indicating whether URL rewriting is forced timezone: UTC # Your PHP installation might require you to manually specify a timezone @@ -11,19 +11,21 @@ timezone: UTC # Your PHP installation might require you to # Theme # theme: default # The name of your custom theme -theme_url: ~ # Pico will try to guess the URL to the themes dir of your installation +theme_url: ~ # Pico will try to guess the URL to the themes dir of your installation; # If this fails, override it here. Example: http://example.com/pico/themes/ theme_config: widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider) twig_config: - cache: false # Enable Twig template caching by specifying a path to a writable directory autoescape: false # Let Twig escape variables by default - debug: false # Enable Twig's debugging mode + strict_variables: false # If set to true, Twig will bail out when unset variables are being used + debug: false # Enable Twig's debug mode + cache: false # Enable Twig template caching by specifying a path to a writable directory + auto_reload: ~ # Recompile Twig templates whenever the source code changes ## # Content # -date_format: %D %T # Pico's default date format +date_format: %D %T # Pico's default date format; # See http://php.net/manual/en/function.strftime.php for more info pages_order_by_meta: author # Sort pages by meta value "author" (set "pages_order_by" to "meta") pages_order_by: alpha # Change how Pico sorts pages ("alpha" for alphabetical order, "date", or "meta") @@ -31,7 +33,7 @@ pages_order: asc # Sort pages in ascending ("asc") or descend content_dir: content/ # The path to Pico's content directory content_ext: .md # The file extension of your Markdown files content_config: - extra: true # Use the Parsedown Extra parser to support extended markup + extra: true # Use the Parsedown Extra parser to support extended markup; # See https://michelf.ca/projects/php-markdown/extra/ for more info breaks: false # A boolean indicating whether breaks in the markup should be reflected in the # parsed contents of the page diff --git a/lib/Pico.php b/lib/Pico.php index c79b5d5..a48cccb 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -943,7 +943,14 @@ class Pico $this->config['theme_url'] = $this->getBaseUrl() . rtrim($this->config['theme_url'], '/') . '/'; } - $defaultTwigConfig = array('cache' => false, 'autoescape' => false, 'debug' => false); + $defaultTwigConfig = array( + 'autoescape' => false, + 'strict_variables' => false, + 'debug' => null, + 'cache' => false, + 'auto_reload' => null + ); + if (!is_array($this->config['twig_config'])) { $this->config['twig_config'] = $defaultTwigConfig; } else { From 8ce3b0c224ed42811f9cd410a7b14862a8071ca7 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 28 Mar 2019 20:12:46 +0100 Subject: [PATCH 07/73] Add debug mode You can enable Pico's debug mode by setting the PICO_DEBUG environment variable. At the moment this just enables Twig's debug mode. --- config/config.yml.template | 1 + lib/Pico.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/config/config.yml.template b/config/config.yml.template index 4d25dcf..638c6d8 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -5,6 +5,7 @@ site_title: Pico # The title of your website base_url: ~ # Pico will try to guess its base URL, if this fails, override it here; # Example: http://example.com/pico/ rewrite_url: ~ # A boolean (true or false) indicating whether URL rewriting is forced +debug: ~ # Set this to true to enable Pico's debug mode timezone: UTC # Your PHP installation might require you to manually specify a timezone ## diff --git a/lib/Pico.php b/lib/Pico.php index a48cccb..50ea42c 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -906,6 +906,7 @@ class Pico 'site_title' => 'Pico', 'base_url' => '', 'rewrite_url' => null, + 'debug' => null, 'timezone' => null, 'theme' => 'default', 'theme_url' => null, @@ -928,6 +929,10 @@ class Pico $this->config['rewrite_url'] = $this->isUrlRewritingEnabled(); } + if ($this->config['debug'] === null) { + $this->config['debug'] = $this->isDebugModeEnabled(); + } + if (!$this->config['timezone']) { // explicitly set a default timezone to prevent a E_NOTICE when no timezone is set; // the `date_default_timezone_get()` function always returns a timezone, at least UTC @@ -959,6 +964,9 @@ class Pico if ($this->config['twig_config']['cache']) { $this->config['twig_config']['cache'] = $this->getAbsolutePath($this->config['twig_config']['cache']); } + if ($this->config['twig_config']['debug'] === null) { + $this->config['twig_config']['debug'] = $this->isDebugModeEnabled(); + } } if (!$this->config['content_dir']) { @@ -2120,6 +2128,29 @@ class Pico return $this->config['rewrite_url']; } + /** + * Returns TRUE if Pico's debug mode is enabled + * + * @return bool TRUE if Pico's debug mode is enabled, FALSE otherwise + */ + public function isDebugModeEnabled() + { + $debugModeEnabled = $this->getConfig('debug'); + if ($debugModeEnabled !== null) { + return $debugModeEnabled; + } + + if (isset($_SERVER['PICO_DEBUG'])) { + $this->config['debug'] = (bool) $_SERVER['PICO_DEBUG']; + } elseif (isset($_SERVER['REDIRECT_PICO_DEBUG'])) { + $this->config['debug'] = (bool) $_SERVER['REDIRECT_PICO_DEBUG']; + } else { + $this->config['debug'] = false; + } + + return $this->config['debug']; + } + /** * Returns the URL to a given page * From f016c8a9375c20bec03c42c182ddcbed07fc50a7 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 30 Apr 2019 15:22:49 +0200 Subject: [PATCH 08/73] Add Pico::getNormalizedPath() --- lib/Pico.php | 88 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index 50ea42c..899869e 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -1155,39 +1155,22 @@ class Pico if (!$requestUrl) { return $contentDir . 'index' . $contentExt; } else { - // prevent content_dir breakouts - $requestUrl = str_replace('\\', '/', $requestUrl); - $requestUrlParts = explode('/', $requestUrl); - - $requestFileParts = array(); - foreach ($requestUrlParts as $requestUrlPart) { - if (($requestUrlPart === '') || ($requestUrlPart === '.')) { - continue; - } elseif ($requestUrlPart === '..') { - array_pop($requestFileParts); - continue; - } - - $requestFileParts[] = $requestUrlPart; - } - - if (!$requestFileParts) { - return $contentDir . 'index' . $contentExt; - } + // normalize path and prevent content_dir breakouts + $requestFile = $this->getNormalizedPath($requestUrl, false, false); // discover the content file to serve - // Note: $requestFileParts neither contains a trailing nor a leading slash - $requestFile = $contentDir . implode('/', $requestFileParts); - if (is_dir($requestFile)) { + if (!$requestFile) { + return $contentDir . 'index' . $contentExt; + } elseif (is_dir($contentDir . $requestFile)) { // if no index file is found, try a accordingly named file in the previous dir // if this file doesn't exist either, show the 404 page, but assume the index // file as being requested (maintains backward compatibility to Pico < 1.0) - $indexFile = $requestFile . '/index' . $contentExt; - if (file_exists($indexFile) || !file_exists($requestFile . $contentExt)) { + $indexFile = $contentDir . $requestFile . '/index' . $contentExt; + if (file_exists($indexFile) || !file_exists($contentDir . $requestFile . $contentExt)) { return $indexFile; } } - return $requestFile . $contentExt; + return $contentDir . $requestFile . $contentExt; } } @@ -2506,6 +2489,61 @@ class Pico return rtrim($path, '/\\') . '/'; } + /** + * Normalizes a path by taking care of '', '.' and '..' parts + * + * @param string $path path to normalize + * @param bool $allowsAbsolutePath whether absolute paths are allowed + * @param bool $endSlash whether to add a trailing slash to the + * normalized path or not (defaults to TRUE) + * + * @return string normalized path + * + * @throws UnexpectedValueException thrown when a absolute path is passed + * although absolute paths aren't allowed + */ + public function getNormalizedPath($path, $allowsAbsolutePath = false, $endSlash = true) + { + $absolutePath = ''; + if (DIRECTORY_SEPARATOR === '\\') { + if (preg_match('/^(?>[a-zA-Z]:\\\\|\\\\\\\\)/', $path, $pathMatches) === 1) { + $absolutePath = $pathMatches[0]; + $path = substr($path, strlen($absolutePath)); + } + } else { + if ($path[0] === '/') { + $absolutePath = '/'; + $path = substr($path, 1); + } + } + + if ($absolutePath && !$allowsAbsolutePath) { + throw new UnexpectedValueException( + 'Argument 1 passed to ' . __METHOD__ . ' must be a relative path, absolute path "' . $path . '" given' + ); + } + + $path = str_replace('\\', '/', $path); + $pathParts = explode('/', $path); + + $resultParts = array(); + foreach ($pathParts as $pathPart) { + if (($pathPart === '') || ($pathPart === '.')) { + continue; + } elseif ($pathPart === '..') { + array_pop($resultParts); + continue; + } + $resultParts[] = $pathPart; + } + + if (!$resultParts) { + return $absolutePath ?: '/'; + } + + return $absolutePath . implode('/', $resultParts) . ($endSlash ? '/' : ''); + } + /** * Triggers events on plugins using the current API version * From bb1b8639bd231b227f3b6c7903e92af68a952a16 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 30 Apr 2019 15:26:31 +0200 Subject: [PATCH 09/73] Add Pico::getUrlFromPath() and Pico::getAbsoluteUrl(), replacing Pico::getBaseThemeUrl() --- lib/Pico.php | 88 +++++++++++++++++++++++++++-------------- plugins/DummyPlugin.php | 1 - 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index 899869e..bf32949 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -941,11 +941,9 @@ class Pico date_default_timezone_set($this->config['timezone']); if (!$this->config['theme_url']) { - $this->config['theme_url'] = $this->getBaseThemeUrl(); - } elseif (preg_match('#^[A-Za-z][A-Za-z0-9+\-.]*://#', $this->config['theme_url'])) { - $this->config['theme_url'] = rtrim($this->config['theme_url'], '/') . '/'; + $this->config['theme_url'] = $this->getUrlFromPath($this->getThemesDir()); } else { - $this->config['theme_url'] = $this->getBaseUrl() . rtrim($this->config['theme_url'], '/') . '/'; + $this->config['thems_url'] = $this->getAbsoluteUrl($this->config['theme_url']); } $defaultTwigConfig = array( @@ -1486,7 +1484,7 @@ class Pico $variables['%base_url%'] = rtrim($this->getBaseUrl(), '/'); // replace %theme_url% - $variables['%theme_url%'] = $this->getBaseThemeUrl() . $this->getConfig('theme'); + $variables['%theme_url%'] = $this->getConfig('theme_url') . $this->getConfig('theme'); // replace %meta.*% if ($meta) { @@ -2006,7 +2004,7 @@ class Pico 'base_dir' => rtrim($this->getRootDir(), '/'), 'base_url' => rtrim($this->getBaseUrl(), '/'), 'theme_dir' => $this->getThemesDir() . $this->getConfig('theme'), - 'theme_url' => $this->getBaseThemeUrl() . $this->getConfig('theme'), + 'theme_url' => $this->getConfig('theme_url') . $this->getConfig('theme'), 'site_title' => $this->getConfig('site_title'), 'meta' => $this->meta, 'content' => $this->content, @@ -2212,41 +2210,46 @@ class Pico } /** - * Returns the URL of the themes folder of this Pico instance + * Returns the URL of a given absolute path within this Pico instance * - * We assume that the themes folder is a arbitrary deep sub folder of the + * We assume that the given path is a arbitrary deep sub folder of the * script's base path (i.e. the directory {@path "index.php"} is in resp. - * the `httpdocs` directory). Usually the script's base path is identical - * to {@see Pico::$rootDir}, but this may aberrate when Pico got installed - * as a composer dependency. However, ultimately it allows us to use - * {@see Pico::getBaseUrl()} as origin of the theme URL. Otherwise Pico - * falls back to the basename of {@see Pico::$themesDir} (i.e. assuming - * that `Pico::$themesDir` is `foo/bar/baz`, the base URL of the themes - * folder will be `baz/`; this ensures BC to Pico < 2.0). Pico's base URL - * always gets prepended appropriately. + * the `httpdocs` directory). If this isn't the case, we check whether it's + * a sub folder of {@see Pico::$rootDir} (what is often identical to the + * script's base path). If this isn't the case either, we fall back to + * the basename of the given folder. This whole process ultimately allows + * us to use {@see Pico::getBaseUrl()} as origin for the URL. * - * @return string the URL of the themes folder + * This method is used to guess Pico's `plugins_url`, `themes_url` and + * `assets_url`. However, guessing might fail, requiring a manual config. + * + * @param $absolutePath + * + * @return string the URL of the given folder */ - public function getBaseThemeUrl() + public function getUrlFromPath($absolutePath) { - $themeUrl = $this->getConfig('theme_url'); - if ($themeUrl) { - return $themeUrl; - } - - if (isset($_SERVER['SCRIPT_FILENAME']) && ($_SERVER['SCRIPT_FILENAME'] !== 'index.php')) { + $basePath = ''; + if (isset($_SERVER['SCRIPT_FILENAME']) && strrpos($_SERVER['SCRIPT_FILENAME'], '/')) { $basePath = dirname($_SERVER['SCRIPT_FILENAME']); $basePath = !in_array($basePath, array('.', '/', '\\'), true) ? $basePath . '/' : '/'; $basePathLength = strlen($basePath); - if (substr($this->getThemesDir(), 0, $basePathLength) === $basePath) { - $this->config['theme_url'] = $this->getBaseUrl() . substr($this->getThemesDir(), $basePathLength); - return $this->config['theme_url']; + if ((substr($absolutePath, 0, $basePathLength) === $basePath) && ($basePath !== '/')) { + return $this->getBaseUrl() . substr($absolutePath, $basePathLength); } } - $this->config['theme_url'] = $this->getBaseUrl() . basename($this->getThemesDir()) . '/'; - return $this->config['theme_url']; + if ($basePath !== $this->getRootDir()) { + $basePath = $this->getRootDir(); + $basePathLength = strlen($basePath); + + if (substr($absolutePath, 0, $basePathLength) === $basePath) { + return $this->getBaseUrl() . substr($absolutePath, $basePathLength); + } + } + + return $this->getBaseUrl() . basename($absolutePath) . '/'; } /** @@ -2544,6 +2547,33 @@ class Pico return $absolutePath . implode('/', $resultParts) . ($endSlash ? '/' : ''); } + /** + * Makes a relative URL absolute to Pico's base URL + * + * Please note that URLs starting with a slash are considered absolute URLs + * even though they don't include a scheme and host. + * + * @param string $url relative or absolute URL + * @param string $baseUrl treat relative URLs relative to the given URL; + * defaults to Pico::getBaseUrl() + * @param bool $endSlash whether to add a trailing slash to the absolute + * URL or not (defaults to TRUE) + * + * @return string absolute URL + */ + public function getAbsoluteUrl($url, $baseUrl = null, $endSlash = true) + { + if ($baseUrl === null) { + $baseUrl = $this->getBaseUrl(); + } + + if (($url[0] !== '/') && !preg_match('#^[A-Za-z][A-Za-z0-9+\-.]*://#', $url)) { + $url = $baseUrl . $url; + } + + return rtrim($url, '/') . ($endSlash ? '/' : ''); + } + /** * Triggers events on plugins using the current API version * diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index effc980..c7ba5db 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -108,7 +108,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @see Pico::getConfig() * @see Pico::getBaseUrl() - * @see Pico::getBaseThemeUrl() * @see Pico::isUrlRewritingEnabled() * * @param array &$config array of config variables From 38bb0a4ac75f13e0d62c4c1dfaed556d637cf61f Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 30 Apr 2019 15:30:00 +0200 Subject: [PATCH 10/73] Various small improvements --- config/config.yml.template | 8 ++++---- lib/Pico.php | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/config/config.yml.template b/config/config.yml.template index 638c6d8..bdaa45e 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -3,7 +3,7 @@ # site_title: Pico # The title of your website base_url: ~ # Pico will try to guess its base URL, if this fails, override it here; - # Example: http://example.com/pico/ + # Example: https://example.com/pico/ rewrite_url: ~ # A boolean (true or false) indicating whether URL rewriting is forced debug: ~ # Set this to true to enable Pico's debug mode timezone: UTC # Your PHP installation might require you to manually specify a timezone @@ -13,7 +13,7 @@ timezone: UTC # Your PHP installation might require you to # theme: default # The name of your custom theme theme_url: ~ # Pico will try to guess the URL to the themes dir of your installation; - # If this fails, override it here. Example: http://example.com/pico/themes/ + # If this fails, override it here. Example: https://example.com/pico/themes/ theme_config: widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider) twig_config: @@ -27,11 +27,11 @@ twig_config: # Content # date_format: %D %T # Pico's default date format; - # See http://php.net/manual/en/function.strftime.php for more info + # See https://php.net/manual/en/function.strftime.php for more info pages_order_by_meta: author # Sort pages by meta value "author" (set "pages_order_by" to "meta") pages_order_by: alpha # Change how Pico sorts pages ("alpha" for alphabetical order, "date", or "meta") pages_order: asc # Sort pages in ascending ("asc") or descending ("desc") order -content_dir: content/ # The path to Pico's content directory +content_dir: ~ # The path to Pico's content directory content_ext: .md # The file extension of your Markdown files content_config: extra: true # Use the Parsedown Extra parser to support extended markup; diff --git a/lib/Pico.php b/lib/Pico.php index bf32949..f306801 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -541,6 +541,8 @@ class Pico * @param string[] $pluginBlacklist class names of plugins not to load * * @return string[] installer names of the loaded plugins + * + * @throws RuntimeException thrown when a plugin couldn't be loaded */ protected function loadComposerPlugins(array $pluginBlacklist = array()) { @@ -703,7 +705,7 @@ class Pico * * @return PicoPluginInterface instance of the loaded plugin * - * @throws RuntimeException thrown when a plugin couldn't be loaded + * @throws RuntimeException thrown when the plugin couldn't be loaded */ public function loadPlugin($plugin) { @@ -904,7 +906,7 @@ class Pico // merge default config $this->config += array( 'site_title' => 'Pico', - 'base_url' => '', + 'base_url' => null, 'rewrite_url' => null, 'debug' => null, 'timezone' => null, @@ -912,6 +914,7 @@ class Pico 'theme_url' => null, 'twig_config' => null, 'date_format' => '%D %T', + 'pages_order_by_meta' => 'author', 'pages_order_by' => 'alpha', 'pages_order' => 'asc', 'content_dir' => null, @@ -1992,8 +1995,7 @@ class Pico /** * Returns the variables passed to the template * - * URLs and paths (namely `base_dir`, `base_url`, `theme_dir` and - * `theme_url`) don't add a trailing slash for historic reasons. + * URLs and paths don't add a trailing slash for historic reasons. * * @return array template variables */ @@ -2145,6 +2147,8 @@ class Pico * "index", passing TRUE (default) will remove this path component * * @return string URL + * + * @throws InvalidArgumentException thrown when invalid arguments got passed */ public function getPageUrl($page, $queryData = null, $dropIndex = true) { @@ -2152,7 +2156,7 @@ class Pico $queryData = http_build_query($queryData, '', '&'); } elseif (($queryData !== null) && !is_string($queryData)) { throw new InvalidArgumentException( - 'Argument 2 passed to ' . get_called_class() . '::getPageUrl() must be of the type array or string, ' + 'Argument 2 passed to ' . __METHOD__ . ' must be of the type array or string, ' . (is_object($queryData) ? get_class($queryData) : gettype($queryData)) . ' given' ); } @@ -2465,15 +2469,15 @@ class Pico /** * Makes a relative path absolute to Pico's root dir * - * This method also guarantees a trailing slash. - * * @param string $path relative or absolute path * @param string $basePath treat relative paths relative to the given path; * defaults to Pico::$rootDir + * @param bool $endSlash whether to add a trailing slash to the absolute + * path or not (defaults to TRUE) * * @return string absolute path */ - public function getAbsolutePath($path, $basePath = null) + public function getAbsolutePath($path, $basePath = null, $endSlash = true) { if ($basePath === null) { $basePath = $this->getRootDir(); @@ -2489,7 +2493,7 @@ class Pico } } - return rtrim($path, '/\\') . '/'; + return rtrim($path, '/\\') . ($endSlash ? '/' : ''); } /** From 33117be981aa9c8acb6b3f91c5513e4a266d7a9d Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 30 Apr 2019 15:34:11 +0200 Subject: [PATCH 11/73] Config: Rename theme_url to themes_url, add plugins_url, assets_url and assets_dir --- config/config.yml.template | 7 ++++++- lib/Pico.php | 43 +++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/config/config.yml.template b/config/config.yml.template index bdaa45e..b16cdd7 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -12,7 +12,7 @@ timezone: UTC # Your PHP installation might require you to # Theme # theme: default # The name of your custom theme -theme_url: ~ # Pico will try to guess the URL to the themes dir of your installation; +themes_url: ~ # Pico will try to guess the URL to the themes dir of your installation; # If this fails, override it here. Example: https://example.com/pico/themes/ theme_config: widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider) @@ -41,10 +41,15 @@ content_config: escape: false # Escape HTML markup in your content files; don't confuse this with some sort of # safe mode, enabling this doesn't allow you to process untrusted user input! auto_urls: true # Automatically link URLs found in your markup +assets_dir: assets/ # The path to Pico's assets directory +assets_url: ~ # Pico will try to guess the URL to the assets dir of your installation; + # If this fails, override it here. Example: https://example.com/pico/assets/ ## # Plugins # +plugins_url: ~ # Pico will try to guess the URL to the plugins dir of your installation; + # If this fails, override it here. Example: https://example.com/pico/plugins/ DummyPlugin.enabled: false # Force the plugin "DummyPlugin" to be disabled ## diff --git a/lib/Pico.php b/lib/Pico.php index f306801..adb1e10 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -910,8 +910,9 @@ class Pico 'rewrite_url' => null, 'debug' => null, 'timezone' => null, + 'plugins_url' => null, 'theme' => 'default', - 'theme_url' => null, + 'themes_url' => null, 'twig_config' => null, 'date_format' => '%D %T', 'pages_order_by_meta' => 'author', @@ -919,7 +920,9 @@ class Pico 'pages_order' => 'asc', 'content_dir' => null, 'content_ext' => '.md', - 'content_config' => null + 'content_config' => null, + 'assets_dir' => 'assets/', + 'assets_url' => null ); if (!$this->config['base_url']) { @@ -943,10 +946,16 @@ class Pico } date_default_timezone_set($this->config['timezone']); - if (!$this->config['theme_url']) { - $this->config['theme_url'] = $this->getUrlFromPath($this->getThemesDir()); + if (!$this->config['plugins_url']) { + $this->config['plugins_url'] = $this->getUrlFromPath($this->getPluginsDir()); } else { - $this->config['thems_url'] = $this->getAbsoluteUrl($this->config['theme_url']); + $this->config['plugins_url'] = $this->getAbsoluteUrl($this->config['plugins_url']); + } + + if (!$this->config['themes_url']) { + $this->config['themes_url'] = $this->getUrlFromPath($this->getThemesDir()); + } else { + $this->config['themes_url'] = $this->getAbsoluteUrl($this->config['themes_url']); } $defaultTwigConfig = array( @@ -989,6 +998,18 @@ class Pico } else { $this->config['content_config'] += $defaultContentConfig; } + + if (!$this->config['assets_dir']) { + $this->config['assets_dir'] = $this->getRootDir() . 'assets/'; + } else { + $this->config['assets_dir'] = $this->getAbsolutePath($this->config['assets_dir']); + } + + if (!$this->config['assets_url']) { + $this->config['assets_url'] = $this->getUrlFromPath($this->config['assets_dir']); + } else { + $this->config['assets_url'] = $this->getAbsoluteUrl($this->config['assets_url']); + } } /** @@ -1486,8 +1507,13 @@ class Pico } $variables['%base_url%'] = rtrim($this->getBaseUrl(), '/'); + // replace %plugins_url%, %themes_url% and %assets_url% + $variables['%plugins_url%'] = rtrim($this->getConfig('plugins_url'), '/'); + $variables['%themes_url%'] = rtrim($this->getConfig('themes_url'), '/'); + $variables['%assets_url%'] = rtrim($this->getConfig('assets_url'), '/'); + // replace %theme_url% - $variables['%theme_url%'] = $this->getConfig('theme_url') . $this->getConfig('theme'); + $variables['%theme_url%'] = $this->getConfig('themes_url') . $this->getConfig('theme'); // replace %meta.*% if ($meta) { @@ -2005,8 +2031,11 @@ class Pico 'config' => $this->getConfig(), 'base_dir' => rtrim($this->getRootDir(), '/'), 'base_url' => rtrim($this->getBaseUrl(), '/'), + 'plugins_url' => rtrim($this->getConfig('plugins_url'), '/'), + 'themes_url' => rtrim($this->getConfig('themes_url'), '/'), + 'assets_url' => rtrim($this->getConfig('assets_url'), '/'), 'theme_dir' => $this->getThemesDir() . $this->getConfig('theme'), - 'theme_url' => $this->getConfig('theme_url') . $this->getConfig('theme'), + 'theme_url' => $this->getConfig('themes_url') . $this->getConfig('theme'), 'site_title' => $this->getConfig('site_title'), 'meta' => $this->meta, 'content' => $this->content, From ad729a99c4b8db986e59d1b2241bef49a394973e Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 30 Apr 2019 15:34:48 +0200 Subject: [PATCH 12/73] Remove base_dir and theme_dir Twig variables These variables aren't really needed in Twig and can still be accessed using $config --- lib/Pico.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index adb1e10..3f9eefc 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2029,12 +2029,10 @@ class Pico { return array( 'config' => $this->getConfig(), - 'base_dir' => rtrim($this->getRootDir(), '/'), 'base_url' => rtrim($this->getBaseUrl(), '/'), 'plugins_url' => rtrim($this->getConfig('plugins_url'), '/'), 'themes_url' => rtrim($this->getConfig('themes_url'), '/'), 'assets_url' => rtrim($this->getConfig('assets_url'), '/'), - 'theme_dir' => $this->getThemesDir() . $this->getConfig('theme'), 'theme_url' => $this->getConfig('themes_url') . $this->getConfig('theme'), 'site_title' => $this->getConfig('site_title'), 'meta' => $this->meta, From f3b5a9224703333bd0a3d953155fec77b95052d9 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 30 Apr 2019 16:55:47 +0200 Subject: [PATCH 13/73] Fix directory separator in Pico::getUrlFromPath() --- lib/Pico.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Pico.php b/lib/Pico.php index 3f9eefc..fd87314 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2260,6 +2260,8 @@ class Pico */ public function getUrlFromPath($absolutePath) { + $absolutePath = str_replace('\\', '/', $absolutePath); + $basePath = ''; if (isset($_SERVER['SCRIPT_FILENAME']) && strrpos($_SERVER['SCRIPT_FILENAME'], '/')) { $basePath = dirname($_SERVER['SCRIPT_FILENAME']); From 3eab6c58d04590da835a81adc07cd04180b1c0fb Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 14 Jul 2019 15:07:12 +0200 Subject: [PATCH 14/73] Bump version to 2.1.0-nightly --- composer.json | 6 +++--- lib/Pico.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 1cb9141..7863dc6 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "require": { "php": ">=5.3.6", "ext-dom": "*", - "twig/twig": "^1.35", + "twig/twig": "^1.36", "symfony/yaml" : "^2.8", "erusev/parsedown": "~1.8.0|1.8.0@beta", "erusev/parsedown-extra": "~0.8.0|0.8.0@beta" @@ -39,8 +39,8 @@ "picocms/composer-installer": "This Composer plugin is responsible for installing Pico plugins and themes using the Composer package manager." }, "require-dev" : { - "phpdocumentor/phpdocumentor": "^2.8", - "squizlabs/php_codesniffer": "^2.4" + "phpdocumentor/phpdocumentor": "^2.9", + "squizlabs/php_codesniffer": "^2.9" }, "autoload": { "psr-0": { diff --git a/lib/Pico.php b/lib/Pico.php index fd87314..f75f320 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -40,7 +40,7 @@ * @author Daniel Rudolf * @link http://picocms.org * @license http://opensource.org/licenses/MIT The MIT License - * @version 2.0 + * @version 2.1 */ class Pico { @@ -49,14 +49,14 @@ class Pico * * @var string */ - const VERSION = '2.0.5-beta.1'; + const VERSION = '2.1.0-nightly'; /** * Pico version ID * * @var int */ - const VERSION_ID = 20005; + const VERSION_ID = 20100; /** * Pico API version From ae82c163692a0a42e1929a71bbcd8676bcfcbf3d Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 14 Jul 2019 15:20:39 +0200 Subject: [PATCH 15/73] composer.json: Add 2.1.x-dev alias for pico-2.1 branch --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 7863dc6..8c42034 100644 --- a/composer.json +++ b/composer.json @@ -52,6 +52,7 @@ "extra": { "branch-alias": { "dev-master": "2.0.x-dev", + "dev-pico-2.1": "2.1.x-dev", "dev-pico-3.0": "3.0.x-dev" } } From 6476d6507d99c1149d997d78bdaadcfeefd6e0a1 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 14 Jul 2019 15:21:12 +0200 Subject: [PATCH 16/73] Re-add deprecated Pico::getBaseThemeUrl() to maintain BC --- lib/Pico.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Pico.php b/lib/Pico.php index f75f320..88102a8 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2240,6 +2240,18 @@ class Pico return substr($path, $contentDirLength, -$contentExtLength) ?: null; } + /** + * Returns the URL of the themes folder of this Pico instance + * + * @deprecated 3.0.0 + * + * @return string + */ + public function getBaseThemeUrl() + { + return $this->getConfig('themes_url'); + } + /** * Returns the URL of a given absolute path within this Pico instance * From 8c8c6e33f73b0710324c3371da932a2eda1057b1 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 14 Jul 2019 15:36:02 +0200 Subject: [PATCH 17/73] composer.json: Remove 3.0.x-dev alias --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 8c42034..db3682f 100644 --- a/composer.json +++ b/composer.json @@ -52,8 +52,7 @@ "extra": { "branch-alias": { "dev-master": "2.0.x-dev", - "dev-pico-2.1": "2.1.x-dev", - "dev-pico-3.0": "3.0.x-dev" + "dev-pico-2.1": "2.1.x-dev" } } } From c9a3f846737ef18443b4efef4a216a2419932a37 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 14 Jul 2019 16:16:57 +0200 Subject: [PATCH 18/73] Sync config/config.yml.template with Pico::loadConfig() --- config/config.yml.template | 4 ++-- lib/Pico.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/config.yml.template b/config/config.yml.template index b16cdd7..174c610 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -6,7 +6,7 @@ base_url: ~ # Pico will try to guess its base URL, if th # Example: https://example.com/pico/ rewrite_url: ~ # A boolean (true or false) indicating whether URL rewriting is forced debug: ~ # Set this to true to enable Pico's debug mode -timezone: UTC # Your PHP installation might require you to manually specify a timezone +timezone: ~ # Your PHP installation might require you to manually specify a timezone ## # Theme @@ -19,7 +19,7 @@ theme_config: twig_config: autoescape: false # Let Twig escape variables by default strict_variables: false # If set to true, Twig will bail out when unset variables are being used - debug: false # Enable Twig's debug mode + debug: ~ # Enable Twig's debug mode cache: false # Enable Twig template caching by specifying a path to a writable directory auto_reload: ~ # Recompile Twig templates whenever the source code changes diff --git a/lib/Pico.php b/lib/Pico.php index 88102a8..2eeb163 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -910,7 +910,6 @@ class Pico 'rewrite_url' => null, 'debug' => null, 'timezone' => null, - 'plugins_url' => null, 'theme' => 'default', 'themes_url' => null, 'twig_config' => null, @@ -922,7 +921,8 @@ class Pico 'content_ext' => '.md', 'content_config' => null, 'assets_dir' => 'assets/', - 'assets_url' => null + 'assets_url' => null, + 'plugins_url' => null ); if (!$this->config['base_url']) { From 641cae849bb9fd93d39d316587df711d986c233e Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 29 Aug 2019 16:42:18 +0200 Subject: [PATCH 19/73] Travis CI: Additionally create .zip release archives --- .build/create-release.sh | 31 +++++++++++++++++++++++-------- .travis.yml | 6 ++++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.build/create-release.sh b/.build/create-release.sh index be836fc..8a799a2 100755 --- a/.build/create-release.sh +++ b/.build/create-release.sh @@ -5,10 +5,15 @@ export PATH="$PICO_TOOLS_DIR:$PATH" . "$PICO_TOOLS_DIR/functions/parse-version.sh.inc" # parameters -ARCHIVE="$1" # release archive file name +ARCHIVE_DIR="$1" # directory to create release archives in +ARCHIVE_FILENAME="$2" # release archive file name (without file extension) -if [ -z "$ARCHIVE" ]; then - echo "Unable to create release archive: No file name specified" >&2 +if [ -z "$ARCHIVE_DIR" ] || [ "$(realpath "$ARCHIVE_DIR")" == "$(realpath "$PICO_BUILD_DIR")" ]; then + echo "Unable to create release archives: Invalid release archive target dir '$ARCHIVE_DIR'" >&2 + exit 1 +fi +if [ -z "$ARCHIVE_FILENAME" ]; then + echo "Unable to create release archives: No release archive file name given" >&2 exit 1 fi @@ -69,14 +74,24 @@ find plugins/ -type d -path 'plugins/*/.git' -print0 | xargs -0 rm -rf echo -# create release archive -echo "Creating release archive '$ARCHIVE'..." +# create release archives +echo "Creating release archive '$ARCHIVE.tar.gz'..." -if [ -e "$ARCHIVE" ]; then - echo "Unable to create release archive: File exists" >&2 +if [ -e "$ARCHIVE_DIR/$ARCHIVE.tar.gz" ]; then + echo "Unable to create release archive: File '$ARCHIVE.tar.gz' exists" >&2 exit 1 fi find . -mindepth 1 -maxdepth 1 -printf '%f\0' \ - | xargs -0 -- tar -czf "$ARCHIVE" -- + | xargs -0 -- tar -czf "$ARCHIVE_DIR/$ARCHIVE.tar.gz" -- +echo + +echo "Creating release archive '$ARCHIVE.zip'..." + +if [ -e "$ARCHIVE_DIR/$ARCHIVE.zip" ]; then + echo "Unable to create release archive: File '$ARCHIVE.zip' exists" >&2 + exit 1 +fi + +zip -q -r "$ARCHIVE_DIR/$ARCHIVE.zip" . echo diff --git a/.travis.yml b/.travis.yml index 3dab6a3..00f6a8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,11 +34,13 @@ jobs: - deploy.sh before_deploy: - '[ "$TRAVIS_TAG" == "v$(php -r "require_once(\"lib/Pico.php\"); echo Pico::VERSION;")" ]' - - create-release.sh "$TRAVIS_BUILD_DIR/pico-release-$TRAVIS_TAG.tar.gz" + - create-release.sh "$TRAVIS_BUILD_DIR" "pico-release-$TRAVIS_TAG" deploy: provider: releases api_key: ${GITHUB_OAUTH_TOKEN} - file: pico-release-$TRAVIS_TAG.tar.gz + file: + - pico-release-$TRAVIS_TAG.tar.gz + - pico-release-$TRAVIS_TAG.zip skip_cleanup: true name: Version ${TRAVIS_TAG:1} draft: true From 8d6e9ac31e4bb7d18212632c1864c99c4c5b938a Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 29 Aug 2019 23:06:27 +0200 Subject: [PATCH 20/73] Replace file_exists() by is_file() --- lib/Pico.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index 2eeb163..2539bec 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -423,7 +423,7 @@ class Pico $this->triggerEvent('onContentLoading'); $hiddenFileRegex = '/(?:^|\/)(?:_|404' . preg_quote($this->getConfig('content_ext'), '/') . '$)/'; - if (file_exists($this->requestFile) && !preg_match($hiddenFileRegex, $this->requestFile)) { + if (is_file($this->requestFile) && !preg_match($hiddenFileRegex, $this->requestFile)) { $this->rawContent = $this->loadFileContent($this->requestFile); } else { $this->triggerEvent('on404ContentLoading'); @@ -547,10 +547,10 @@ class Pico protected function loadComposerPlugins(array $pluginBlacklist = array()) { $composerPlugins = array(); - if (file_exists($this->getVendorDir() . 'vendor/pico-plugin.php')) { + if (is_file($this->getVendorDir() . 'vendor/pico-plugin.php')) { // composer root package $composerPlugins = require($this->getVendorDir() . 'vendor/pico-plugin.php') ?: array(); - } elseif (file_exists($this->getVendorDir() . '../../../vendor/pico-plugin.php')) { + } elseif (is_file($this->getVendorDir() . '../../../vendor/pico-plugin.php')) { // composer dependency package $composerPlugins = require($this->getVendorDir() . '../../../vendor/pico-plugin.php') ?: array(); } @@ -637,7 +637,7 @@ class Pico $className = preg_replace('/^[0-9]+-/', '', $file); $pluginFile = $file . '/' . $className . '.php'; - if (!file_exists($this->getPluginsDir() . $pluginFile)) { + if (!is_file($this->getPluginsDir() . $pluginFile)) { throw new RuntimeException( "Unable to load plugin '" . $className . "' from '" . $pluginFile . "': File not found" ); @@ -891,7 +891,7 @@ class Pico // load main config file (config/config.yml) $this->config = is_array($this->config) ? $this->config : array(); - if (file_exists($this->getConfigDir() . 'config.yml')) { + if (is_file($this->getConfigDir() . 'config.yml')) { $this->config += $loadConfigClosure($this->getConfigDir() . 'config.yml'); } @@ -1188,7 +1188,7 @@ class Pico // if this file doesn't exist either, show the 404 page, but assume the index // file as being requested (maintains backward compatibility to Pico < 1.0) $indexFile = $contentDir . $requestFile . '/index' . $contentExt; - if (file_exists($indexFile) || !file_exists($contentDir . $requestFile . $contentExt)) { + if (is_file($indexFile) || !is_file($contentDir . $requestFile . $contentExt)) { return $indexFile; } } @@ -1247,11 +1247,11 @@ class Pico $errorFileDir = dirname($errorFileDir); $errorFile = $errorFileDir . '/404' . $contentExt; - if (file_exists($contentDir . $errorFile)) { + if (is_file($contentDir . $errorFile)) { return $this->loadFileContent($contentDir . $errorFile); } } - } elseif (file_exists($contentDir . '404' . $contentExt)) { + } elseif (is_file($contentDir . '404' . $contentExt)) { // provided that the requested file is not in the regular // content directory, fallback to Pico's global `404.md` return $this->loadFileContent($contentDir . '404' . $contentExt); From 715cb834311cd51b512cba50b8886afc6695e841 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 29 Aug 2019 23:07:16 +0200 Subject: [PATCH 21/73] Improve class docs of Pico::getBaseThemeUrl() and Pico::getUrlFromPath() --- lib/Pico.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index 2539bec..48d243b 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2243,6 +2243,8 @@ class Pico /** * Returns the URL of the themes folder of this Pico instance * + * @see Pico::getUrlFromPath() + * * @deprecated 3.0.0 * * @return string @@ -2266,7 +2268,7 @@ class Pico * This method is used to guess Pico's `plugins_url`, `themes_url` and * `assets_url`. However, guessing might fail, requiring a manual config. * - * @param $absolutePath + * @param string $absolutePath the absolute path to interpret * * @return string the URL of the given folder */ From 6e6d80c044a81b6107ef7410733946a1b6cf0a51 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 29 Aug 2019 23:10:35 +0200 Subject: [PATCH 22/73] Add Pico::substituteUrl() and `url` Twig filter Allows theme developers and users to use URL placeholders like `%base_url%` in meta headers, e.g. to include images. --- lib/Pico.php | 30 ++++++++++++++++++++++++++++++ lib/PicoTwigExtension.php | 3 ++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index 48d243b..fcb9170 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2240,6 +2240,36 @@ class Pico return substr($path, $contentDirLength, -$contentExtLength) ?: null; } + /** + * Substitutes URL placeholders (e.g. %base_url%) + * + * This method is registered as the `url` Twig filter and often used to + * allow users to specify absolute URLs in meta data utilizing the known + * URL placeholders `%base_url%`, `%plugins_url%`, `%themes_url%`, + * `%assets_url%` and `%theme_url%`. + * + * Don't confuse this with the `link` Twig filter, which takes a page ID as + * parameter. However, you can indeed use this method to create page URLs, + * e.g. `{{ "%base_url%?sub/page"|url }}`. + * + * @param string $url URL with placeholders + * + * @return string URL with replaced placeholders + */ + public function substituteUrl($url) + { + $variables = array( + '%base_url%?' => $this->getBaseUrl() . (!$this->isUrlRewritingEnabled() ? '?' : ''), + '%base_url%' => rtrim($this->getBaseUrl(), '/'), + '%plugins_url%' => rtrim($this->getConfig('plugins_url'), '/'), + '%themes_url%' => rtrim($this->getConfig('themes_url'), '/'), + '%assets_url%' => rtrim($this->getConfig('assets_url'), '/'), + '%theme_url%' => $this->getConfig('themes_url') . $this->getTheme() + ); + + return str_replace(array_keys($variables), $variables, $url); + } + /** * Returns the URL of the themes folder of this Pico instance * diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index a774184..bfe01fe 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -75,7 +75,8 @@ class PicoTwigExtension extends Twig_Extension 'markdown' => new Twig_SimpleFilter('markdown', array($this, 'markdownFilter')), 'map' => new Twig_SimpleFilter('map', array($this, 'mapFilter')), 'sort_by' => new Twig_SimpleFilter('sort_by', array($this, 'sortByFilter')), - 'link' => new Twig_SimpleFilter('link', array($this->pico, 'getPageUrl')) + 'link' => new Twig_SimpleFilter('link', array($this->pico, 'getPageUrl')), + 'url' => new Twig_SimpleFilter('url', array($this->pico, 'substituteUrl')) ); } From 56659ab95d42430a4adbfbddf5b460282dc119ac Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 9 Sep 2019 15:10:20 +0200 Subject: [PATCH 23/73] Build system: Don't use Composer for dev dependencies PHP_CodeSniffer and phpDocumentor are external tools which should never affect Pico's environment, thus we rather use PHARs in the future. --- .build/install.sh | 22 +++++++++++++++++++++- composer.json | 4 ---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.build/install.sh b/.build/install.sh index 5dde051..de25de5 100755 --- a/.build/install.sh +++ b/.build/install.sh @@ -9,11 +9,31 @@ case "$1" in echo "Synchronizing package index files..." sudo apt-get update - echo "Installing packages..." + echo "Installing cloc..." sudo apt-get install -y cloc + + echo "Installing phpDocumentor..." + curl --location --output "$PICO_TOOLS_DIR/phpdoc" \ + "https://github.com/phpDocumentor/phpDocumentor2/releases/latest/download/phpDocumentor.phar" + chmod +x "$PICO_TOOLS_DIR/phpdoc" ;; esac +echo "Installing PHP_CodeSniffer..." +if [ "$(php -r 'echo PHP_VERSION_ID;')" -ge 50400 ]; then + PHPCS_DOWNLOAD="https://github.com/squizlabs/PHP_CodeSniffer/releases/latest/download/" +else + PHPCS_DOWNLOAD="https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.9.2/" +fi + +curl --location --output "$PICO_TOOLS_DIR/phpcs" \ + "$PHPCS_DOWNLOAD/phpcs.phar" +chmod +x "$PICO_TOOLS_DIR/phpcs" + +curl --location --output "$PICO_TOOLS_DIR/phpcbf" \ + "$PHPCS_DOWNLOAD/phpcbf.phar" +chmod +x "$PICO_TOOLS_DIR/phpcbf" + echo # setup composer diff --git a/composer.json b/composer.json index db3682f..016b823 100644 --- a/composer.json +++ b/composer.json @@ -38,10 +38,6 @@ "picocms/pico-deprecated": "PicoDeprecated's purpose is to maintain backward compatibility to older versions of Pico.", "picocms/composer-installer": "This Composer plugin is responsible for installing Pico plugins and themes using the Composer package manager." }, - "require-dev" : { - "phpdocumentor/phpdocumentor": "^2.9", - "squizlabs/php_codesniffer": "^2.9" - }, "autoload": { "psr-0": { "Pico": "lib/", From eca06a38a9856c666c99ab579f85d268573cdbdb Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 11 Sep 2019 11:29:09 +0200 Subject: [PATCH 24/73] phpDoc class docs: Remove superflous @return void --- lib/AbstractPicoPlugin.php | 6 ---- lib/Pico.php | 24 ---------------- lib/PicoPluginInterface.php | 4 --- plugins/DummyPlugin.php | 56 ------------------------------------- 4 files changed, 90 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 41f954f..a2e1f9c 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -212,8 +212,6 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * * @param bool $recursive enable required plugins automatically * - * @return void - * * @throws RuntimeException thrown when a dependency fails */ protected function checkDependencies($recursive) @@ -264,8 +262,6 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * * @param bool $recursive disabled dependant plugins automatically * - * @return void - * * @throws RuntimeException thrown when a dependency fails */ protected function checkDependants($recursive) @@ -327,8 +323,6 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * newer API versions, what requires some special (version specific) * precaution and is therefore usually not the case. * - * @return void - * * @throws RuntimeException thrown when the plugin's and Pico's API aren't * compatible */ diff --git a/lib/Pico.php b/lib/Pico.php index fcb9170..6fead44 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -508,8 +508,6 @@ class Pico * @see Pico::getPlugin() * @see Pico::getPlugins() * - * @return void - * * @throws RuntimeException thrown when a plugin couldn't be loaded */ protected function loadPlugins() @@ -610,8 +608,6 @@ class Pico * * @param string[] $pluginBlacklist class names of plugins not to load * - * @return void - * * @throws RuntimeException thrown when a plugin couldn't be loaded */ protected function loadLocalPlugins(array $pluginBlacklist = array()) @@ -766,8 +762,6 @@ class Pico * Marc J. Schmidt's Topological Sort / Dependency resolver in PHP * @see https://github.com/marcj/topsort.php/blob/1.1.0/src/Implementations/ArraySort.php * \MJS\TopSort\Implementations\ArraySort class - * - * @return void */ protected function sortPlugins() { @@ -876,8 +870,6 @@ class Pico * * @see Pico::setConfig() * @see Pico::getConfig() - * - * @return void */ protected function loadConfig() { @@ -1028,8 +1020,6 @@ class Pico * * @param array $config array with config variables * - * @return void - * * @throws LogicException thrown if Pico already started processing */ public function setConfig(array $config) @@ -1102,8 +1092,6 @@ class Pico * `/pico/?someBooleanParam=` or `/pico/?index&someBooleanParam` instead. * * @see Pico::getRequestUrl() - * - * @return void */ protected function evaluateRequestUrl() { @@ -1595,8 +1583,6 @@ class Pico * @see Pico::sortPages() * @see Pico::discoverPageSiblings() * @see Pico::getPages() - * - * @return void */ protected function readPages() { @@ -1680,8 +1666,6 @@ class Pico * * @see Pico::readPages() * @see Pico::getPages() - * - * @return void */ protected function sortPages() { @@ -1761,8 +1745,6 @@ class Pico * * @see Pico::readPages() * @see Pico::getPages() - * - * @return void */ protected function discoverPageSiblings() { @@ -1812,8 +1794,6 @@ class Pico * @see Pico::getCurrentPage() * @see Pico::getPreviousPage() * @see Pico::getNextPage() - * - * @return void */ protected function discoverCurrentPage() { @@ -1900,8 +1880,6 @@ class Pico * non-iterable data structure with Pico 3.0. * * @see Pico::getPageTree() - * - * @return void */ protected function buildPageTree() { @@ -2668,8 +2646,6 @@ class Pico * * @param string $eventName name of the event to trigger * @param array $params optional parameters to pass - * - * @return void */ public function triggerEvent($eventName, array $params = array()) { diff --git a/lib/PicoPluginInterface.php b/lib/PicoPluginInterface.php index 13a30dd..2c16a98 100644 --- a/lib/PicoPluginInterface.php +++ b/lib/PicoPluginInterface.php @@ -42,8 +42,6 @@ interface PicoPluginInterface * * @param string $eventName name of the triggered event * @param array $params passed parameters - * - * @return void */ public function handleEvent($eventName, array $params); @@ -63,8 +61,6 @@ interface PicoPluginInterface * @param bool $auto enable or disable to fulfill a dependency. This * parameter is optional and defaults to FALSE. * - * @return void - * * @throws RuntimeException thrown when a dependency fails */ public function setEnabled($enabled, $recursive = true, $auto = false); diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index c7ba5db..94727fb 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -79,8 +79,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getPlugins() * * @param object[] $plugins loaded plugin instances - * - * @return void */ public function onPluginsLoaded(array $plugins) { @@ -95,8 +93,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getPlugins() * * @param object $plugin loaded plugin instance - * - * @return void */ public function onPluginManuallyLoaded($plugin) { @@ -111,8 +107,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::isUrlRewritingEnabled() * * @param array &$config array of config variables - * - * @return void */ public function onConfigLoaded(array &$config) { @@ -125,8 +119,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getRequestUrl() * * @param string &$url part of the URL describing the requested contents - * - * @return void */ public function onRequestUrl(&$url) { @@ -140,8 +132,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getRequestFile() * * @param string &$file absolute path to the content file to serve - * - * @return void */ public function onRequestFile(&$file) { @@ -153,8 +143,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @see Pico::loadFileContent() * @see DummyPlugin::onContentLoaded() - * - * @return void */ public function onContentLoading() { @@ -166,8 +154,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @see Pico::load404Content() * @see DummyPlugin::on404ContentLoaded() - * - * @return void */ public function on404ContentLoading() { @@ -182,8 +168,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::is404Content() * * @param string &$rawContent raw file contents - * - * @return void */ public function on404ContentLoaded(&$rawContent) { @@ -202,8 +186,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::is404Content() * * @param string &$rawContent raw file contents - * - * @return void */ public function onContentLoaded(&$rawContent) { @@ -215,8 +197,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @see Pico::parseFileMeta() * @see DummyPlugin::onMetaParsed() - * - * @return void */ public function onMetaParsing() { @@ -230,8 +210,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getFileMeta() * * @param string[] &$meta parsed meta data - * - * @return void */ public function onMetaParsed(array &$meta) { @@ -245,8 +223,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::substituteFileContent() * @see DummyPlugin::onContentPrepared() * @see DummyPlugin::onContentParsed() - * - * @return void */ public function onContentParsing() { @@ -261,8 +237,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see DummyPlugin::onContentParsed() * * @param string &$markdown Markdown contents of the requested page - * - * @return void */ public function onContentPrepared(&$markdown) { @@ -277,8 +251,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getFileContent() * * @param string &$content parsed contents (HTML) of the requested page - * - * @return void */ public function onContentParsed(&$content) { @@ -290,8 +262,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @see DummyPlugin::onPagesDiscovered() * @see DummyPlugin::onPagesLoaded() - * - * @return void */ public function onPagesLoading() { @@ -313,8 +283,6 @@ class DummyPlugin extends AbstractPicoPlugin * @param string $id relative path to the content file * @param bool|null $skipPage set this to TRUE to remove this page from the * pages array, otherwise leave it unchanged - * - * @return void */ public function onSinglePageLoading($id, &$skipPage) { @@ -333,8 +301,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @param string $id relative path to the content file * @param string &$rawContent raw file contents - * - * @return void */ public function onSinglePageContent($id, &$rawContent) { @@ -351,8 +317,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see DummyPlugin::onSinglePageContent() * * @param array &$pageData data of the loaded page - * - * @return void */ public function onSinglePageLoaded(array &$pageData) { @@ -371,8 +335,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see DummyPlugin::onPagesLoaded() * * @param array[] &$pages list of all known pages - * - * @return void */ public function onPagesDiscovered(array &$pages) { @@ -391,8 +353,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getPages() * * @param array[] &$pages sorted list of all known pages - * - * @return void */ public function onPagesLoaded(array &$pages) { @@ -414,8 +374,6 @@ class DummyPlugin extends AbstractPicoPlugin * @param array|null &$currentPage data of the page being served * @param array|null &$previousPage data of the previous page * @param array|null &$nextPage data of the next page - * - * @return void */ public function onCurrentPageDiscovered( array &$currentPage = null, @@ -434,8 +392,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getPageTree() * * @param array &$pageTree page tree - * - * @return void */ public function onPageTreeBuilt(array &$pageTree) { @@ -449,8 +405,6 @@ class DummyPlugin extends AbstractPicoPlugin * * @param string &$templateName file name of the template * @param array &$twigVariables template variables - * - * @return void */ public function onPageRendering(&$templateName, array &$twigVariables) { @@ -463,8 +417,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see DummyPlugin::onPageRendering() * * @param string &$output contents which will be sent to the user - * - * @return void */ public function onPageRendered(&$output) { @@ -479,8 +431,6 @@ class DummyPlugin extends AbstractPicoPlugin * @param string[] &$headers list of known meta header fields; the array * key specifies the YAML key to search for, the array value is later * used to access the found value - * - * @return void */ public function onMetaHeaders(array &$headers) { @@ -493,8 +443,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getYamlParser() * * @param \Symfony\Component\Yaml\Parser &$yamlParser YAML parser instance - * - * @return void */ public function onYamlParserRegistered(\Symfony\Component\Yaml\Parser &$yamlParser) { @@ -507,8 +455,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getParsedown() * * @param Parsedown &$parsedown Parsedown instance - * - * @return void */ public function onParsedownRegistered(Parsedown &$parsedown) { @@ -521,8 +467,6 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::getTwig() * * @param Twig_Environment &$twig Twig instance - * - * @return void */ public function onTwigRegistered(Twig_Environment &$twig) { From d72bc24ab3f05247d9908233408a4b4efb29f280 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 11 Sep 2019 12:03:58 +0200 Subject: [PATCH 25/73] Remove PicoPluginInterface::__construct() It doesn't really matter how the current Pico instance is injected into the plugin unless PicoPluginInterface::getPico() is implemented --- lib/AbstractPicoPlugin.php | 4 +++- lib/PicoPluginInterface.php | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index a2e1f9c..72e0ca5 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -77,7 +77,9 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface private $dependants; /** - * @see PicoPluginInterface::__construct() + * Constructs a new instance of a Pico plugin + * + * @param Pico $pico current instance of Pico */ public function __construct(Pico $pico) { diff --git a/lib/PicoPluginInterface.php b/lib/PicoPluginInterface.php index 2c16a98..7716275 100644 --- a/lib/PicoPluginInterface.php +++ b/lib/PicoPluginInterface.php @@ -30,13 +30,6 @@ */ interface PicoPluginInterface { - /** - * Constructs a new instance of a Pico plugin - * - * @param Pico $pico current instance of Pico - */ - public function __construct(Pico $pico); - /** * Handles a event that was triggered by Pico * From bbccb374de07c7055baf2e89892a915b4dd81dda Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 11 Sep 2019 12:05:33 +0200 Subject: [PATCH 26/73] phpDoc class docs: Use {@inheritDoc} in AbstractPicoPlugin --- lib/AbstractPicoPlugin.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 72e0ca5..73ca4ba 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -87,7 +87,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::handleEvent() + * {@inheritDoc} */ public function handleEvent($eventName, array $params) { @@ -122,7 +122,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::setEnabled() + * {@inheritDoc} */ public function setEnabled($enabled, $recursive = true, $auto = false) { @@ -138,7 +138,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::isEnabled() + * {@inheritDoc} */ public function isEnabled() { @@ -146,7 +146,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::isStatusChanged() + * {@inheritDoc} */ public function isStatusChanged() { @@ -154,7 +154,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::getPico() + * {@inheritDoc} */ public function getPico() { @@ -250,7 +250,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::getDependencies() + * {@inheritDoc} */ public function getDependencies() { @@ -295,7 +295,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } /** - * @see PicoPluginInterface::getDependants() + * {@inheritDoc} */ public function getDependants() { From 581a3a06093a9ce76eb7054110befb26d070f091 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 11 Sep 2019 12:05:56 +0200 Subject: [PATCH 27/73] Add AbstractPicoPlugin::configEnabled() --- lib/AbstractPicoPlugin.php | 46 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 73ca4ba..3346778 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -93,25 +93,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface { // plugins can be enabled/disabled using the config if ($eventName === 'onConfigLoaded') { - $pluginEnabled = $this->getConfig(get_called_class() . '.enabled'); - if ($pluginEnabled !== null) { - $this->setEnabled($pluginEnabled); - } else { - $pluginEnabled = $this->getPluginConfig('enabled'); - if ($pluginEnabled !== null) { - $this->setEnabled($pluginEnabled); - } elseif ($this->enabled) { - $this->setEnabled($this->enabled, true, true); - } elseif ($this->enabled === null) { - // make sure dependencies are already fulfilled, - // otherwise the plugin needs to be enabled manually - try { - $this->setEnabled(true, false, true); - } catch (RuntimeException $e) { - $this->enabled = false; - } - } - } + $this->configEnabled(); } if ($this->isEnabled() || ($eventName === 'onPluginsLoaded')) { @@ -121,6 +103,32 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface } } + /** + * Enables or disables this plugin depending on Pico's config + */ + protected function configEnabled() + { + $pluginEnabled = $this->getPico()->getConfig(get_called_class() . '.enabled'); + if ($pluginEnabled !== null) { + $this->setEnabled($pluginEnabled); + } else { + $pluginEnabled = $this->getPluginConfig('enabled'); + if ($pluginEnabled !== null) { + $this->setEnabled($pluginEnabled); + } elseif ($this->enabled) { + $this->setEnabled(true, true, true); + } elseif ($this->enabled === null) { + // make sure dependencies are already fulfilled, + // otherwise the plugin needs to be enabled manually + try { + $this->setEnabled(true, false, true); + } catch (RuntimeException $e) { + $this->enabled = false; + } + } + } + } + /** * {@inheritDoc} */ From fd97c70502654f482d9fb96e5edac3c3f2d53630 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 11 Sep 2019 12:06:40 +0200 Subject: [PATCH 28/73] Deprecated AbstractPicoPlugin::__call() in favour of PicoPluginInterface::getPico() --- lib/AbstractPicoPlugin.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 3346778..5246e15 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -31,7 +31,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::getPico() * @var Pico */ - private $pico; + protected $pico; /** * Boolean indicating if this plugin is enabled (TRUE) or disabled (FALSE) @@ -184,7 +184,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface */ public function getPluginConfig($configName = null, $default = null) { - $pluginConfig = $this->getConfig(get_called_class(), array()); + $pluginConfig = $this->getPico()->getConfig(get_called_class(), array()); if ($configName === null) { return $pluginConfig; @@ -196,7 +196,9 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface /** * Passes all not satisfiable method calls to Pico * - * @see Pico + * @see PicoPluginInterface::getPico() + * + * @deprecated 3.0.0 * * @param string $methodName name of the method to call * @param array $params parameters to pass @@ -228,7 +230,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface { foreach ($this->getDependencies() as $pluginName) { try { - $plugin = $this->getPlugin($pluginName); + $plugin = $this->getPico()->getPlugin($pluginName); } catch (RuntimeException $e) { throw new RuntimeException( "Unable to enable plugin '" . get_called_class() . "': " @@ -309,7 +311,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface { if ($this->dependants === null) { $this->dependants = array(); - foreach ($this->getPlugins() as $pluginName => $plugin) { + foreach ($this->getPico()->getPlugins() as $pluginName => $plugin) { // only plugins which implement PicoPluginInterface support dependencies if ($plugin instanceof PicoPluginInterface) { $dependencies = $plugin->getDependencies(); From 17aba015132bef9ff6c7da230bbfc9a42a311d18 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 11 Sep 2019 12:07:48 +0200 Subject: [PATCH 29/73] Various small improvements --- config/config.yml.template | 6 +++--- lib/AbstractPicoPlugin.php | 4 ++-- lib/Pico.php | 8 ++++---- lib/PicoPluginInterface.php | 4 ++-- plugins/DummyPlugin.php | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config/config.yml.template b/config/config.yml.template index 174c610..d36a5fa 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -14,9 +14,9 @@ timezone: ~ # Your PHP installation might require you to theme: default # The name of your custom theme themes_url: ~ # Pico will try to guess the URL to the themes dir of your installation; # If this fails, override it here. Example: https://example.com/pico/themes/ -theme_config: +theme_config: # Additional theme-specific config widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider) -twig_config: +twig_config: # Twig template engine config autoescape: false # Let Twig escape variables by default strict_variables: false # If set to true, Twig will bail out when unset variables are being used debug: ~ # Enable Twig's debug mode @@ -33,7 +33,7 @@ pages_order_by: alpha # Change how Pico sorts pages ("alpha" for a pages_order: asc # Sort pages in ascending ("asc") or descending ("desc") order content_dir: ~ # The path to Pico's content directory content_ext: .md # The file extension of your Markdown files -content_config: +content_config: # Parsedown Markdown parser config extra: true # Use the Parsedown Extra parser to support extended markup; # See https://michelf.ca/projects/php-markdown/extra/ for more info breaks: false # A boolean indicating whether breaks in the markup should be reflected in the diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 5246e15..17bf483 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -74,7 +74,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * @see PicoPluginInterface::getDependants() * @var object[]|null */ - private $dependants; + protected $dependants; /** * Constructs a new instance of a Pico plugin @@ -330,7 +330,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * * Pico automatically adds a dependency to {@see PicoDeprecated} when the * plugin's API is older than Pico's API. {@see PicoDeprecated} furthermore - * throws a exception when it can't provide compatibility in such cases. + * throws a exception if it can't provide compatibility in such cases. * However, we still have to decide whether this plugin is compatible to * newer API versions, what requires some special (version specific) * precaution and is therefore usually not the case. diff --git a/lib/Pico.php b/lib/Pico.php index 6fead44..aa23f0c 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -212,7 +212,7 @@ class Pico * List of known meta headers * * @see Pico::getMetaHeaders() - * @var string[]|null + * @var array|null */ protected $metaHeaders; @@ -710,7 +710,7 @@ class Pico if (class_exists($className)) { $plugin = new $className($this); } else { - throw new RuntimeException("Unable to load plugin '" . $className . "': Class not found"); + throw new RuntimeException("Unable to load plugin '" . $className . "': Class not found"); } } @@ -858,7 +858,7 @@ class Pico } /** - * Loads the config.yml and any other *.yml from Pico::$configDir + * Loads config.yml and any other *.yml from Pico::$configDir * * After loading {@path "config/config.yml"}, Pico proceeds with any other * existing `config/*.yml` file in alphabetical order. The file order is @@ -1641,7 +1641,7 @@ class Pico 'time' => &$meta['time'], 'date' => &$meta['date'], 'date_formatted' => &$meta['date_formatted'], - 'hidden' => (preg_match('/(?:^|\/)_/', $id) || $meta['hidden']), + 'hidden' => ($meta['hidden'] || preg_match('/(?:^|\/)_/', $id)), 'raw_content' => &$rawContent, 'meta' => &$meta ); diff --git a/lib/PicoPluginInterface.php b/lib/PicoPluginInterface.php index 7716275..6add326 100644 --- a/lib/PicoPluginInterface.php +++ b/lib/PicoPluginInterface.php @@ -95,11 +95,11 @@ interface PicoPluginInterface public function getDependants(); /** - * Returns the plugins instance of Pico + * Returns the plugin's instance of Pico * * @see Pico * - * @return Pico the plugins instance of Pico + * @return Pico the plugin's instance of Pico */ public function getPico(); } diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index 94727fb..5198a24 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -36,8 +36,8 @@ class DummyPlugin extends AbstractPicoPlugin * Usually you should remove this class property (or set it to NULL) to * leave the decision whether this plugin should be enabled or disabled by * default up to Pico. If all the plugin's dependenies are fulfilled (see - * {@see self::$dependsOn}), Pico enables the plugin by default. Otherwise - * the plugin is silently disabled. + * {@see DummyPlugin::$dependsOn}), Pico enables the plugin by default. + * Otherwise the plugin is silently disabled. * * If this plugin should never be disabled *silently* (e.g. when dealing * with security-relevant stuff like access control, or similar), set this From cd3d3dcec563613b7fc24edc921137bfe84d4013 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 12 Sep 2019 12:45:35 +0200 Subject: [PATCH 30/73] Bump API version Due to ad729a99c4b8db986e59d1b2241bef49a394973e and 33117be981aa9c8acb6b3f91c5513e4a266d7a9d --- lib/Pico.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index aa23f0c..3118432 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -63,7 +63,7 @@ class Pico * * @var int */ - const API_VERSION = 2; + const API_VERSION = 3; /** * Sort files in alphabetical ascending order From d0b637f686a9406ee474cf7121815c5bc722112c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 12 Sep 2019 13:46:22 +0200 Subject: [PATCH 31/73] Build system: Use PHP 5.6 for deployment due to broken phpDocumentor v2.9 phpDocumentor v3.0 (currently in alpha) is broken, too, generating class docs without a single working link --- .phpdoc.xml | 4 ++-- .travis.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.phpdoc.xml b/.phpdoc.xml index f924f31..8f6456e 100644 --- a/.phpdoc.xml +++ b/.phpdoc.xml @@ -1,5 +1,5 @@ - + <![CDATA[Pico API Documentation]]> .build/phpdoc.cache @@ -30,4 +30,4 @@ vendor/* - + diff --git a/.travis.yml b/.travis.yml index 00f6a8f..255871d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ jobs: # Deployment stage - stage: deploy + php: 5.6 sudo: required install: - '[ "$TRAVIS_PULL_REQUEST" == "false" ] || travis_terminate 0' From c1113a780cf9a3aa265244da56039314fa00379f Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 12 Sep 2019 14:00:58 +0200 Subject: [PATCH 32/73] Fix @deprecated notice for Pico::getBaseThemeUrl() and AbstractPicoPlugin::__call() --- lib/AbstractPicoPlugin.php | 2 +- lib/Pico.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index 17bf483..c586c29 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -198,7 +198,7 @@ abstract class AbstractPicoPlugin implements PicoPluginInterface * * @see PicoPluginInterface::getPico() * - * @deprecated 3.0.0 + * @deprecated 2.1.0 * * @param string $methodName name of the method to call * @param array $params parameters to pass diff --git a/lib/Pico.php b/lib/Pico.php index 3118432..ae56782 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2253,7 +2253,7 @@ class Pico * * @see Pico::getUrlFromPath() * - * @deprecated 3.0.0 + * @deprecated 2.1.0 * * @return string */ From b27b4f388a34210a94ab59fc12faececb5435d4c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 22 Sep 2019 18:49:37 +0200 Subject: [PATCH 33/73] :tada: Add Pico theme API versioning and add pico-theme.yml - Add pico-theme.yml with a theme's API version, theme-specific default Twig config, registering theme-specific custom meta headers and defaults for Pico's `theme_config` config - Add new `onThemeLoading(&$theme)` and `onThemeLoaded($theme, $themeApiVersion, &$themeConfig)` events - Enable Twig autoescaping by default --- config/config.yml.template | 3 +- lib/Pico.php | 173 ++++++++++++++++++++++++++++++++----- plugins/DummyPlugin.php | 29 +++++++ 3 files changed, 180 insertions(+), 25 deletions(-) diff --git a/config/config.yml.template b/config/config.yml.template index d36a5fa..21eaeca 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -17,8 +17,9 @@ themes_url: ~ # Pico will try to guess the URL to the them theme_config: # Additional theme-specific config widescreen: false # Default theme: Use more horicontal space (i.e. make the site container wider) twig_config: # Twig template engine config - autoescape: false # Let Twig escape variables by default + autoescape: html # Let Twig escape variables by default strict_variables: false # If set to true, Twig will bail out when unset variables are being used + charset: utf-8 # The charset used by Twig templates debug: ~ # Enable Twig's debug mode cache: false # Enable Twig template caching by specifying a path to a writable directory auto_reload: ~ # Recompile Twig templates whenever the source code changes diff --git a/lib/Pico.php b/lib/Pico.php index ae56782..40d736b 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -168,6 +168,29 @@ class Pico */ protected $config; + /** + * Theme in use + * + * @see Pico::getTheme() + * @var string + */ + protected $theme; + + /** + * API version of the current theme + * + * @see Pico::getThemeApiVersion() + * @var int + */ + protected $themeApiVersion; + + /** + * Additional meta headers of the current theme + * + * @var array|null + */ + protected $themeMetaHeaders; + /** * Part of the URL describing the requested contents * @@ -411,6 +434,16 @@ class Pico throw new RuntimeException('Invalid content directory "' . $this->getConfig('content_dir') . '"'); } + // load theme + $this->theme = $this->config['theme']; + $this->triggerEvent('onThemeLoading', array(&$this->theme)); + + $this->loadTheme(); + $this->triggerEvent( + 'onThemeLoaded', + array($this->theme, $this->themeApiVersion, &$this->config['theme_config']) + ); + // evaluate request url $this->evaluateRequestUrl(); $this->triggerEvent('onRequestUrl', array(&$this->requestUrl)); @@ -903,6 +936,8 @@ class Pico 'debug' => null, 'timezone' => null, 'theme' => 'default', + 'theme_config' => null, + 'theme_meta' => null, 'themes_url' => null, 'twig_config' => null, 'date_format' => '%D %T', @@ -950,27 +985,6 @@ class Pico $this->config['themes_url'] = $this->getAbsoluteUrl($this->config['themes_url']); } - $defaultTwigConfig = array( - 'autoescape' => false, - 'strict_variables' => false, - 'debug' => null, - 'cache' => false, - 'auto_reload' => null - ); - - if (!is_array($this->config['twig_config'])) { - $this->config['twig_config'] = $defaultTwigConfig; - } else { - $this->config['twig_config'] += $defaultTwigConfig; - - if ($this->config['twig_config']['cache']) { - $this->config['twig_config']['cache'] = $this->getAbsolutePath($this->config['twig_config']['cache']); - } - if ($this->config['twig_config']['debug'] === null) { - $this->config['twig_config']['debug'] = $this->isDebugModeEnabled(); - } - } - if (!$this->config['content_dir']) { // try to guess the content directory if (is_file($this->getRootDir() . 'content/index' . $this->config['content_ext'])) { @@ -1056,6 +1070,113 @@ class Pico } } + /** + * Loads a theme's config file (pico-theme.yml) + * + * @see Pico::getTheme() + * @see Pico::getThemeApiVersion() + */ + protected function loadTheme() + { + $themeConfig = array(); + + // load theme config from pico-theme.yml + $themeConfigFile = $this->getThemesDir() . $this->getTheme() . '/pico-theme.yml'; + if (is_file($themeConfigFile)) { + $yamlParser = $this->getYamlParser(); + $loadConfigClosure = function ($configFile) use ($yamlParser) { + $yaml = file_get_contents($configFile); + $config = $yamlParser->parse($yaml); + return is_array($config) ? $config : array(); + }; + + $themeConfig = $loadConfigClosure($themeConfigFile); + } + + $themeConfig += array( + 'api_version' => null, + 'meta' => array(), + 'twig_config' => array() + ); + + // theme API version + if (preg_match('/^[0-9]+$/', $themeConfig['api_version'])) { + $this->themeApiVersion = (int) $themeConfig['api_version']; + } else { + $this->themeApiVersion = 0; + } + + unset($themeConfig['api_version']); + + // twig config + $themeTwigConfig = array('autoescape' => 'html', 'strict_variables' => false, 'charset' => 'utf-8'); + foreach ($themeTwigConfig as $key => $_) { + if (isset($themeConfig['twig_config'][$key])) { + $themeTwigConfig[$key] = $themeConfig['twig_config'][$key]; + } + } + + unset($themeConfig['twig_config']); + + $defaultTwigConfig = array('debug' => null, 'cache' => false, 'auto_reload' => null); + $this->config['twig_config'] = array_merge($defaultTwigConfig, $themeTwigConfig, $this->config['twig_config']); + + if ($this->config['twig_config']['autoescape'] === true) { + $this->config['twig_config']['autoescape'] = 'html'; + } + if ($this->config['twig_config']['cache']) { + $this->config['twig_config']['cache'] = $this->getAbsolutePath($this->config['twig_config']['cache']); + } + if ($this->config['twig_config']['debug'] === null) { + $this->config['twig_config']['debug'] = $this->isDebugModeEnabled(); + } + + // meta headers + $this->themeMetaHeaders = is_array($themeConfig['meta']) ? $themeConfig['meta'] : array(); + unset($themeConfig['meta']); + + // theme config + if (!is_array($this->config['theme_config'])) { + $this->config['theme_config'] = $themeConfig; + } else { + $this->config['theme_config'] += $themeConfig; + } + + // check for theme compatibility + if (!isset($this->plugins['PicoDeprecated']) && ($this->themeApiVersion < static::API_VERSION)) { + throw new RuntimeException( + 'Current theme "' . $this->theme . '" uses API version ' . $this->themeApiVersion . ', but Pico ' + . 'provides API version ' . static::API_VERSION . ' and PicoDeprecated isn\'t loaded' + ); + } + } + + /** + * Returns the name of the current theme + * + * @see Pico::loadTheme() + * @see Pico::getThemeApiVersion() + * + * @return string + */ + public function getTheme() + { + return $this->theme; + } + + /** + * Returns the API version of the current theme + * + * @see Pico::loadTheme() + * @see Pico::getTheme() + * + * @return int + */ + public function getThemeApiVersion() + { + return $this->themeApiVersion; + } + /** * Evaluates the requested URL * @@ -1305,6 +1426,10 @@ class Pico 'Hidden' => 'hidden' ); + if ($this->themeMetaHeaders) { + $this->metaHeaders += $this->themeMetaHeaders; + } + $this->triggerEvent('onMetaHeaders', array(&$this->metaHeaders)); } @@ -1501,7 +1626,7 @@ class Pico $variables['%assets_url%'] = rtrim($this->getConfig('assets_url'), '/'); // replace %theme_url% - $variables['%theme_url%'] = $this->getConfig('themes_url') . $this->getConfig('theme'); + $variables['%theme_url%'] = $this->getConfig('themes_url') . $this->getTheme(); // replace %meta.*% if ($meta) { @@ -1964,7 +2089,7 @@ class Pico if ($this->twig === null) { $twigConfig = $this->getConfig('twig_config'); - $twigLoader = new Twig_Loader_Filesystem($this->getThemesDir() . $this->getConfig('theme')); + $twigLoader = new Twig_Loader_Filesystem($this->getThemesDir() . $this->getTheme()); $this->twig = new Twig_Environment($twigLoader, $twigConfig); $this->twig->addExtension(new PicoTwigExtension($this)); @@ -2011,7 +2136,7 @@ class Pico 'plugins_url' => rtrim($this->getConfig('plugins_url'), '/'), 'themes_url' => rtrim($this->getConfig('themes_url'), '/'), 'assets_url' => rtrim($this->getConfig('assets_url'), '/'), - 'theme_url' => $this->getConfig('themes_url') . $this->getConfig('theme'), + 'theme_url' => $this->getConfig('themes_url') . $this->getTheme(), 'site_title' => $this->getConfig('site_title'), 'meta' => $this->meta, 'content' => $this->content, diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index 5198a24..17225f4 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -113,6 +113,35 @@ class DummyPlugin extends AbstractPicoPlugin // your code } + /** + * Triggered before Pico loads its theme + * + * @see Pico::loadTheme() + * @see DummyPlugin::onThemeLoaded() + * + * @param string $theme name of current theme + */ + public function onThemeLoading(&$theme) + { + // your code + } + + /** + * Triggered after Pico loaded its theme + * + * @see DummyPlugin::onThemeLoading() + * @see Pico::getTheme() + * @see Pico::getThemeApiVersion() + * + * @param string $theme name of current theme + * @param int $themeApiVersion API version of the theme + * @param array $themeConfig config array of the theme + */ + public function onThemeLoaded($theme, $themeApiVersion, array &$themeConfig) + { + // your code + } + /** * Triggered after Pico has evaluated the request URL * From 681ad2715870df3bf5e052bccc34eb9e8f90aaa9 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 22 Sep 2019 18:55:35 +0200 Subject: [PATCH 34/73] :tada: Add Twig pages function This function should be used most of the time when dealing with Pico's pages array, as it allows one to easily traverse Pico's pages tree (see `Pico::getPageTree()`) to retrieve a subset of Pico's pages array in a very convenient and performant way. --- lib/PicoTwigExtension.php | 137 +++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index bfe01fe..1c840f4 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -91,7 +91,8 @@ class PicoTwigExtension extends Twig_Extension { return array( 'url_param' => new Twig_SimpleFunction('url_param', array($this, 'urlParamFunction')), - 'form_param' => new Twig_SimpleFunction('form_param', array($this, 'formParamFunction')) + 'form_param' => new Twig_SimpleFunction('form_param', array($this, 'formParamFunction')), + 'pages' => new Twig_SimpleFunction('pages', array($this, 'pagesFunction')) ); } @@ -340,4 +341,138 @@ class PicoTwigExtension extends Twig_Extension return $this->pico->getFormParameter($name, $filter, $options, $flags); } + + /** + * Returns all pages within a particular branch of Pico's page tree + * + * This function should be used most of the time when dealing with Pico's + * pages array, as it allows one to easily traverse Pico's pages tree + * ({@see Pico::getPageTree()}) to retrieve a subset of Pico's pages array + * in a very convenient and performant way. + * + * The function's default parameters are `$start = ""`, `$depth = 0`, + * `$depthOffset = 0` and `$offset = 1`. A positive `$offset` is equivalent + * to `$depth = $depth + $offset`, `$depthOffset = $depthOffset + $offset` + * and `$offset = 0`. + * + * Consequently the default `$start = ""`, `$depth = 0`, `$depthOffset = 0` + * and `$offset = 1` is equivalent to `$depth = 1`, `$depthOffset = 1` and + * `$offset = 0`. `$start = ""` instruct the function to start from the + * root node (i.e. the node of Pico's main index page at `index.md`). + * `$depth` tells the function what pages to return. In this example, + * `$depth = 1` matches the start node (i.e. the zeroth generation) and all + * its descendant pages until the first generation (i.e. the start node's + * children). `$depthOffset` instructs the function to exclude some of the + * older generations. `$depthOffset = 1` specifically tells the function + * to exclude the zeroth generation, so that the function returns all of + * Pico's main index page's direct child pages (like `sub/index.md` and + * `page.md`, but not `sub/page.md`) only. + * + * Passing `$depthOffset = -1` only is the same as passing `$start = ""`, + * `$depth = 1`, `$depthOffset = 0` and `$offset = 0`. The only difference + * is that `$depthOffset` won't exclude the zeroth generation, so that the + * function returns Pico's main index page as well as all of its direct + * child pages. + * + * Passing `$depth = 0`, `$depthOffset = -2` and `$offset = 2` is the same + * as passing `$depth = 2`, `$depthOffset = 0` and `$offset = 0`. Both will + * return the zeroth, first and second generation of pages. For Pico's main + * index page this would be `index.md` (0th gen), `sub/index.md` (1st gen), + * `sub/page.md` (2nd gen) and `page.md` (1st gen). If you want to return + * 2nd gen pages only, pass `$offset = 2` only (with implicit `$depth = 0` + * and `$depthOffset = 0` it's the same as `$depth = 2`, `$depthOffset = 2` + * and `$offset = 0`). + * + * Instead of an integer you can also pass `$depth = null`. This is the + * same as passing an infinitely large number as `$depth`, so that this + * function simply returns all descendant pages. Consequently passing + * `$start = ""`, `$depth = null`, `$depthOffset = 0` and `$offset = 0` + * returns Pico's full pages array. + * + * If `$depth` is negative after taking `$offset` into consideration, the + * function will throw a {@see Twig_Error_Runtime} exception, since this + * would simply make no sense and is likely an error. Passing a negative + * `$depthOffset` is equivalent to passing `$depthOffset = 0`. + * + * But what about a negative `$offset`? Passing `$offset = -1` instructs + * the function not to start from the given `$start` node, but its parent + * node. Consequently `$offset = -2` instructs the function to use the + * `$start` node's grandparent node. Obviously this won't make any sense + * for Pico's root node, but just image `$start = "sub/index"`. Passing + * this together with `$offset = -1` is equivalent to `$start = ""` and + * `$offset = 0`. + * + * @param string $start name of the node to start from + * @param int|null $depth return pages until the given maximum depth; + * pass NULL to return all descendant pages; defaults to 0 + * @param int $depthOffset start returning pages from the given + * minimum depth; defaults to 0 + * @param int $offset ascend (positive) or descend (negative) the + * given number of branches before returning pages; defaults to 1 + * + * @return array[] the data of the matched pages + * + * @throws Twig_Error_Runtime + */ + public function pagesFunction($start = '', $depth = 0, $depthOffset = 0, $offset = 1) + { + $start = (string) $start; + if (basename($start) === 'index') { + $start = dirname($start); + } + + for (; $offset < 0; $offset++) { + if (in_array($start, array('', '.', '/'), true)) { + $offset = 0; + break; + } + + $start = dirname($start); + } + + $depth = ($depth !== null) ? $depth + $offset : null; + $depthOffset = $depthOffset + $offset; + + if (($depth !== null) && ($depth < 0)) { + throw new Twig_Error_Runtime('The pages function doesn\'t support negative depths'); + } + + $pageTree = $this->getPico()->getPageTree(); + if (in_array($start, array('', '.', '/'), true)) { + if (($depth === null) && ($depthOffset <= 0)) { + return $this->getPico()->getPages(); + } + + $startNode = isset($pageTree['']['/']) ? $pageTree['']['/'] : null; + } else { + $branch = dirname($start); + $branch = ($branch !== '.') ? $branch : '/'; + $node = (($branch !== '/') ? $branch . '/' : '') . basename($start); + $startNode = isset($pageTree[$branch][$node]) ? $pageTree[$branch][$node] : null; + } + + if (!$startNode) { + return array(); + } + + $getPagesClosure = function ($nodes, $depth, $depthOffset) use (&$getPagesClosure) { + $pages = array(); + foreach ($nodes as $node) { + if (isset($node['page']) && ($depthOffset <= 0)) { + $pages[$node['page']['id']] = &$node['page']; + } + if (isset($node['children']) && ($depth > 0)) { + $pages += $getPagesClosure($node['children'], $depth - 1, $depthOffset - 1); + } + } + + return $pages; + }; + + return $getPagesClosure( + array($startNode), + ($depth !== null) ? $depth : INF, + $depthOffset + ); + } } From 92a8a299f8e5d6fdbc343afa6132ac1d93981e90 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 1 Oct 2019 13:28:21 +0200 Subject: [PATCH 35/73] Travis CI: Add PHP 7.4, improve deployment logging --- .build/install.sh | 4 ++++ .travis.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.build/install.sh b/.build/install.sh index de25de5..9e047f8 100755 --- a/.build/install.sh +++ b/.build/install.sh @@ -3,19 +3,23 @@ set -e # setup build system echo "Installing build dependencies..." +echo case "$1" in "--deploy") echo "Synchronizing package index files..." sudo apt-get update + echo echo "Installing cloc..." sudo apt-get install -y cloc + echo echo "Installing phpDocumentor..." curl --location --output "$PICO_TOOLS_DIR/phpdoc" \ "https://github.com/phpDocumentor/phpDocumentor2/releases/latest/download/phpDocumentor.phar" chmod +x "$PICO_TOOLS_DIR/phpdoc" + echo ;; esac diff --git a/.travis.yml b/.travis.yml index 255871d..a658cf1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ jobs: - php: 7.1 - php: 7.2 - php: 7.3 + - php: 7.4 - php: nightly - php: hhvm-3.27 # until Sep 2019 - php: hhvm-3.30 # until Nov 2019 From 7684fc455a72323cd4230fd0a151aed065df48c3 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 1 Oct 2019 13:29:06 +0200 Subject: [PATCH 36/73] Improve index.php error message for a missing 'vendor/autoload.php' --- index.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index 45ecdd2..0d13d6d 100644 --- a/index.php +++ b/index.php @@ -18,7 +18,11 @@ if (is_file(__DIR__ . '/vendor/autoload.php')) { // composer dependency package require_once(__DIR__ . '/../../../vendor/autoload.php'); } else { - die("Cannot find 'vendor/autoload.php'. Run `composer install`."); + die( + "Cannot find 'vendor/autoload.php'. If you're using a composer-based Pico install, run `composer install`. " + . "If you're rather trying to use one of Pico's pre-built release packages, make sure to download Pico's " + . "latest release package named 'pico-release-v*.tar.gz' (don't download a source code package)." + ); } // instance Pico From 87ced8c8bd2fb892a1a9a6f8656c9892d0153fc9 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Tue, 1 Oct 2019 13:29:16 +0200 Subject: [PATCH 37/73] Improve phpDoc class docs --- lib/PicoTwigExtension.php | 4 ++++ plugins/DummyPlugin.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index 1c840f4..18fa5ca 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -130,6 +130,8 @@ class PicoTwigExtension extends Twig_Extension * $item['foo']['bar'] values) * * @return array mapped values + * + * @throws Twig_Error_Runtime */ public function mapFilter($var, $mapKeyPath) { @@ -170,6 +172,8 @@ class PicoTwigExtension extends Twig_Extension * these items * * @return array sorted array + * + * @throws Twig_Error_Runtime */ public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom') { diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index 17225f4..b17d51c 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -119,7 +119,7 @@ class DummyPlugin extends AbstractPicoPlugin * @see Pico::loadTheme() * @see DummyPlugin::onThemeLoaded() * - * @param string $theme name of current theme + * @param string &$theme name of current theme */ public function onThemeLoading(&$theme) { @@ -135,7 +135,7 @@ class DummyPlugin extends AbstractPicoPlugin * * @param string $theme name of current theme * @param int $themeApiVersion API version of the theme - * @param array $themeConfig config array of the theme + * @param array &$themeConfig config array of the theme */ public function onThemeLoaded($theme, $themeApiVersion, array &$themeConfig) { From 6ffbbec689bdadbe0f37820e5b7882c20c40f6f5 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 12 Oct 2019 15:01:16 +0200 Subject: [PATCH 38/73] Improve theme API version retrieval --- lib/Pico.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index 40d736b..f44f42f 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -1100,7 +1100,7 @@ class Pico ); // theme API version - if (preg_match('/^[0-9]+$/', $themeConfig['api_version'])) { + if (is_int($themeConfig['api_version']) || preg_match('/^[0-9]+$/', $themeConfig['api_version'])) { $this->themeApiVersion = (int) $themeConfig['api_version']; } else { $this->themeApiVersion = 0; From 87bcff16548b7ddc00a186f3661071174bac8670 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 12 Oct 2019 15:51:17 +0200 Subject: [PATCH 39/73] Various small improvements --- lib/Pico.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index f44f42f..1026c4e 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2582,11 +2582,10 @@ class Pico public function getFiles($directory, $fileExtension = '', $order = self::SORT_ASC) { $directory = rtrim($directory, '/'); + $fileExtensionLength = strlen($fileExtension); $result = array(); - // scandir() reads files in alphabetical order $files = scandir($directory, $order); - $fileExtensionLength = strlen($fileExtension); if ($files !== false) { foreach ($files as $file) { // exclude hidden files/dirs starting with a .; this also excludes the special dirs . and .. @@ -2675,9 +2674,9 @@ class Pico /** * Normalizes a path by taking care of '', '.' and '..' parts * - * @param string $path path to normalize - * @param bool $allowsAbsolutePath whether absolute paths are allowed - * @param bool $endSlash whether to add a trailing slash to the + * @param string $path path to normalize + * @param bool $allowAbsolutePath whether absolute paths are allowed + * @param bool $endSlash whether to add a trailing slash to the * normalized path or not (defaults to TRUE) * * @return string normalized path @@ -2685,7 +2684,7 @@ class Pico * @throws UnexpectedValueException thrown when a absolute path is passed * although absolute paths aren't allowed */ - public function getNormalizedPath($path, $allowsAbsolutePath = false, $endSlash = true) + public function getNormalizedPath($path, $allowAbsolutePath = false, $endSlash = true) { $absolutePath = ''; if (DIRECTORY_SEPARATOR === '\\') { @@ -2700,7 +2699,7 @@ class Pico } } - if ($absolutePath && !$allowsAbsolutePath) { + if ($absolutePath && !$allowAbsolutePath) { throw new UnexpectedValueException( 'Argument 1 passed to ' . __METHOD__ . ' must be a relative path, absolute path "' . $path . '" given' ); @@ -2743,12 +2742,8 @@ class Pico */ public function getAbsoluteUrl($url, $baseUrl = null, $endSlash = true) { - if ($baseUrl === null) { - $baseUrl = $this->getBaseUrl(); - } - if (($url[0] !== '/') && !preg_match('#^[A-Za-z][A-Za-z0-9+\-.]*://#', $url)) { - $url = $baseUrl . $url; + $url = (($baseUrl !== null) ? $baseUrl : $this->getBaseUrl()) . $url; } return rtrim($url, '/') . ($endSlash ? '/' : ''); From d95c9d370807f231825a056a8646f88c97ab11e4 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 12 Oct 2019 19:54:06 +0200 Subject: [PATCH 40/73] Add content-sample/theme.md The purpose of `theme.md` is to aid theme development - on this page you'll find basically every format that is possible with Markdown. If you develop a theme, you should make sure that all examples below show decent. The page doesn't show up in the website's menu due to `hidden: true` in the page's YAML header. --- content-sample/index.md | 4 + content-sample/theme.md | 191 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 content-sample/theme.md diff --git a/content-sample/index.md b/content-sample/index.md index 04b77a0..0a702f7 100644 --- a/content-sample/index.md +++ b/content-sample/index.md @@ -53,6 +53,10 @@ Below we've shown some examples of locations and their corresponding URLs: content/sub/page.md ?sub/page + + content/theme.md + ?theme (hidden in menu) + content/a/very/long/url.md diff --git a/content-sample/theme.md b/content-sample/theme.md new file mode 100644 index 0000000..647193b --- /dev/null +++ b/content-sample/theme.md @@ -0,0 +1,191 @@ +--- +title: Theme Styling Test +hidden: true +--- + +Theme Styling Test +================== + +This is `theme.md` in Pico's content directory. This page doesn't show up in the website's menu due to `hidden: true` in the page's YAML header. The purpose of this page is to aid theme development - below you'll find basically every format that is possible with Markdown. If you develop a theme, you should make sure that all examples below show decent. + +Text +---- + +**Lorem ipsum dolor sit amet,** consectetur adipisici elit, *sed eiusmod tempor* incidunt ut labore et dolore magna aliqua.[^1] ~~Ut enim ad minim veniam,~~ quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat.[^2] [Quis aute iure reprehenderit][Link] in voluptate velit esse cillum dolore eu fugiat nulla pariatur. `Excepteur` sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +[![Placeholder Logo by logoipsum ](data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzcuNzggMTAwIj48dGl0bGU+bG9nby01PC90aXRsZT48cGF0aCBkPSJNNDYuMjMsNDEuMzloMlY1NS4zMWg2djJoLThabTE0LjIzLDBhNiw2LDAsMSwxLTYsNkE2LDYsMCwwLDEsNjAuNDYsNDEuMzlabTAsMTBhNCw0LDAsMSwwLTQtNEE0LDQsMCwwLDAsNjAuNDYsNTEuMzdabS00LDMuOTRoOHYyaC04Wm0yNS42LDJoLTJWNTUuOWE3LjY5LDcuNjksMCwwLDEtNC40LDEuMzksOCw4LDAsMSwxLDYtMTMuMjNsLTEuNDcsMS4zMWE2LDYsMCwxLDAtNC40NiwxMCw1Ljc3LDUuNzcsMCwwLDAsNC4zOC0ySDc0Ljcxdi0ybDcuMzcsMFptNy4zNi0xNS45YTYsNiwwLDEsMS02LDZBNiw2LDAsMCwxLDg5LjQ0LDQxLjM5Wm0wLDEwYTQsNCwwLDEsMC00LTRBNCw0LDAsMCwwLDg5LjQ0LDUxLjM3Wm0tNCwzLjk0aDh2MmgtOFpNOTcuNTUsNDEuMzloMnYxNS45aC0yWm00Ljc3LDAsMiwwYTEuNDcsMS40NywwLDAsMSwuMzEsMCw2LDYsMCwxLDEsMCwxMS45M2gtLjMxdjRoLTJabTIsMnY3LjkxaC4zMWE0LDQsMCwxLDAsMC03LjkzQTEuNDcsMS40NywwLDAsMCwxMDQuMzEsNDMuNDRabTE0LjQxLS44NS0xLjQ4LDEuMTdhMS45NCwxLjk0LDAsMCwwLTMuMzMsMS4xM1Y0NWMwLC44OC42MSwxLjQ3LDEuOCwxLjU1LDQuNTguMjgsNi42OCwyLjQzLDYuNjgsNS4yN1Y1MmE1LjksNS45LDAsMCwxLTEwLjE3LDMuNDJsMS41My0xLjE4YTMuNiwzLjYsMCwwLDAsMi44LDEuMTRBMy43NCwzLjc0LDAsMCwwLDEyMC40OCw1MnYtLjE0YzAtMi42Ny0yLjYyLTMuMTYtNC44OS0zLjM4LTIuMS0uMi0zLjYxLTEuNTEtMy42MS0zLjRWNDVhMy44NSwzLjg1LDAsMCwxLDMuOTMtMy41NkEzLjc0LDMuNzQsMCwwLDEsMTE4LjcyLDQyLjU5Wm0xMS41LDEyLjcyYzIuNDksMCw0LjQ2LTIuNjksNC40Ni02di04aDJsMCw4YzAsNC40LTIuODgsNy45NS02LjQyLDcuOTVzLTYuNDItMy41NS02LjQyLTcuOTV2LThoMnY4QzEyNS43Nyw1Mi42MiwxMjcuNzQsNTUuMzEsMTMwLjIyLDU1LjMxWm0yMS44MSwyaC0yVjQ3LjM2bC00LjYxLDYtNC42MS02djkuOTNoLTJWNDEuMzlsNi42LDguNjksNi42LTguNjlaIiBzdHlsZT0iZmlsbDojMzk0MTQ5Ii8+PHBhdGggZD0iTTM2LjYzLDM5YTUuNDQsNS40NCwwLDAsMC0xMC44OCwwVjU1LjQ1bDQuNDgsMy4yMXY3LjgyaDEuOTJWNTguNjZsNC40OC0zLjIxWm0tMS45Miw5LjItMi41NiwyLjU2di0yLjRsMi41Ni0yLjU2Wm0tNy0yLjQsMi41NiwyLjU2djIuNGwtMi41Ni0yLjU2Wm03LTIuNzItMy41MiwzLjUyTDI3LjY3LDQzdi0yLjRsMy41MiwzLjUyLDMuNTItMy41MlptLTMuNTItNy42YTMuNTQsMy41NCwwLDAsMSwzLjQsMi42MWwtMy40LDMuMzktMy40LTMuMzlBMy41NCwzLjU0LDAsMCwxLDMxLjE5LDM1LjQ0Wm0tMy41MiwxOVY1MC44OGwyLjU2LDIuNTZWNTYuM1ptNC40OCwxLjgzVjUzLjQ0bDIuNTYtMi41NnYzLjU5WiIgc3R5bGU9ImZpbGw6IzM5NDE0OSIvPjwvc3ZnPg==)](%base_url% "Placeholder Logo by logoipsum ") + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +Headings +-------- + +# h1 + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +## h2 + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +### h3 + +Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +#### h4 + +Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +##### h5 + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + +###### h6 + +At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + +Horizontal line +--------------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +--- + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +List +---- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +* Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + 1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + 2. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + 3. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. +* Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. + - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + - Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + 1. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + 2. At vero eos et accusam et justo duo dolores et ea rebum. + 1. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet + 2. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. + 3. At vero eos et accusam et justo duo dolores et ea rebum. + +Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat. + +Definition list +--------------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +Duis autem +: Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +Lorem ipsum dolor sit amet +: Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +Ut wisi enim ad minim veniam +: Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. +: Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + +Blockquote +---------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +> Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse +> molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero +> eros et accumsan et iusto odio dignissim qui blandit praesent luptatum +> zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum +> dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod +> tincidunt ut laoreet dolore magna aliquam erat volutpat. +> +> > Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit +> > lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure +> > dolor in hendrerit in vulputate velit esse molestie consequat, vel illum +> > dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio +> > dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te +> > feugait nulla facilisi. +> +> Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet +> doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut +> laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, +> quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex +> ea commodo consequat. + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + +Code block +---------- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +``` + + + + This is a title + + +

Hello world!

+ + +``` + +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +Table +----- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +Lorem ipsum | Duis autem vel eum | Ut wisi enim ad minim veniam +----------- | ------------------ | ---------------------------- +**Duis autem vel eum iriure dolor** in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. | *Lorem ipsum dolor sit amet,* consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. | ~~Ut wisi enim ad minim veniam,~~ quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. +[Duis autem vel eum iriure dolor][Link] in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. | `Nam liber tempor` cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. | Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. +Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. | | Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis. + +At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + +Forms +----- + +Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +
+ Legend + + + + +
+ +
+ + + +
+
+
+ +
+ +Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. + +*[Lorem ipsum]: Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. + +[Link]: %base_url% "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat." + +[^1]: Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +[^2]: Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. From 1d5aba46afe9333b65a98e55498a5ee871d034f6 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 20 Oct 2019 15:16:40 +0200 Subject: [PATCH 41/73] Support content files with UTF-8 BOM Resolves #461 --- lib/Pico.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index 1026c4e..b5e3278 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -1477,7 +1477,7 @@ class Pico public function parseFileMeta($rawContent, array $headers) { $meta = array(); - $pattern = "/^(\/(\*)|---)[[:blank:]]*(?:\r)?\n" + $pattern = "/^(?:\xEF\xBB\xBF)?(\/(\*)|---)[[:blank:]]*(?:\r)?\n" . "(?:(.*?)(?:\r)?\n)?(?(2)\*\/|---)[[:blank:]]*(?:(?:\r)?\n|$)/s"; if (preg_match($pattern, $rawContent, $rawMetaMatches) && isset($rawMetaMatches[3])) { $meta = $this->getYamlParser()->parse($rawMetaMatches[3]) ?: array(); @@ -1581,7 +1581,7 @@ class Pico public function prepareFileContent($rawContent, array $meta = array()) { // remove meta header - $metaHeaderPattern = "/^(\/(\*)|---)[[:blank:]]*(?:\r)?\n" + $metaHeaderPattern = "/^(?:\xEF\xBB\xBF)?(\/(\*)|---)[[:blank:]]*(?:\r)?\n" . "(?:(.*?)(?:\r)?\n)?(?(2)\*\/|---)[[:blank:]]*(?:(?:\r)?\n|$)/s"; $markdown = preg_replace($metaHeaderPattern, '', $rawContent, 1); From 9c00ac4191a070cb133baa2aafb7bb4f6e17d29b Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 20 Oct 2019 15:54:31 +0200 Subject: [PATCH 42/73] Travis CI: Remove not-yet-released PHP 7.4 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a658cf1..255871d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,6 @@ jobs: - php: 7.1 - php: 7.2 - php: 7.3 - - php: 7.4 - php: nightly - php: hhvm-3.27 # until Sep 2019 - php: hhvm-3.30 # until Nov 2019 From d2573c5df55712b22a1faf98d4dbe3e249f41bf2 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 20 Oct 2019 19:17:42 +0200 Subject: [PATCH 43/73] Fix $this->config['twig_config'] handling in Pico::loadTheme() --- lib/Pico.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index b5e3278..be71755 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -1083,14 +1083,9 @@ class Pico // load theme config from pico-theme.yml $themeConfigFile = $this->getThemesDir() . $this->getTheme() . '/pico-theme.yml'; if (is_file($themeConfigFile)) { - $yamlParser = $this->getYamlParser(); - $loadConfigClosure = function ($configFile) use ($yamlParser) { - $yaml = file_get_contents($configFile); - $config = $yamlParser->parse($yaml); - return is_array($config) ? $config : array(); - }; - - $themeConfig = $loadConfigClosure($themeConfigFile); + $themeConfigYaml = file_get_contents($themeConfigFile); + $themeConfig = $this->getYamlParser()->parse($themeConfigYaml); + $themeConfig = is_array($themeConfig) ? $themeConfig : array(); } $themeConfig += array( @@ -1119,6 +1114,7 @@ class Pico unset($themeConfig['twig_config']); $defaultTwigConfig = array('debug' => null, 'cache' => false, 'auto_reload' => null); + $this->config['twig_config'] = is_array($this->config['twig_config']) ? $this->config['twig_config'] : array(); $this->config['twig_config'] = array_merge($defaultTwigConfig, $themeTwigConfig, $this->config['twig_config']); if ($this->config['twig_config']['autoescape'] === true) { From cb3bdd149f74786f195f4002341298df5c0c5289 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 24 Oct 2019 12:02:23 +0200 Subject: [PATCH 44/73] Mark Twig markdown filter as HTML safe --- lib/PicoTwigExtension.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index 18fa5ca..c0861f7 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -72,7 +72,11 @@ class PicoTwigExtension extends Twig_Extension public function getFilters() { return array( - 'markdown' => new Twig_SimpleFilter('markdown', array($this, 'markdownFilter')), + 'markdown' => new Twig_SimpleFilter( + 'markdown', + array($this, 'markdownFilter'), + array('is_safe' => array('html')) + ), 'map' => new Twig_SimpleFilter('map', array($this, 'mapFilter')), 'sort_by' => new Twig_SimpleFilter('sort_by', array($this, 'sortByFilter')), 'link' => new Twig_SimpleFilter('link', array($this->pico, 'getPageUrl')), From be0812fb55a6ad2620c43ad13bc178e10cb9ca82 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 24 Oct 2019 12:05:08 +0200 Subject: [PATCH 45/73] Mark Twig content filter as HTML safe --- lib/Pico.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index be71755..c8c514a 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2098,17 +2098,21 @@ class Pico // this is the reason why we can't register this filter as part of PicoTwigExtension $pico = $this; $pages = &$this->pages; - $this->twig->addFilter(new Twig_SimpleFilter('content', function ($page) use ($pico, &$pages) { - if (isset($pages[$page])) { - $pageData = &$pages[$page]; - if (!isset($pageData['content'])) { - $pageData['content'] = $pico->prepareFileContent($pageData['raw_content'], $pageData['meta']); - $pageData['content'] = $pico->parseFileContent($pageData['content']); + $this->twig->addFilter(new Twig_SimpleFilter( + 'content', + function ($page) use ($pico, &$pages) { + if (isset($pages[$page])) { + $pageData = &$pages[$page]; + if (!isset($pageData['content'])) { + $pageData['content'] = $pico->prepareFileContent($pageData['raw_content'], $pageData['meta']); + $pageData['content'] = $pico->parseFileContent($pageData['content']); + } + return $pageData['content']; } - return $pageData['content']; - } - return null; - })); + return null; + }, + array('is_safe' => array('html')) + )); // trigger onTwigRegistration event $this->triggerEvent('onTwigRegistered', array(&$this->twig)); From 23000af64ee2e9a8f93393a69637735c6bef1bd0 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Thu, 24 Oct 2019 13:03:38 +0200 Subject: [PATCH 46/73] Add $singleLine param to Twig markdown parser This allows you to parse just a single line of Markdown, i.e. the parsed output won't include a HTML paragraph element. --- lib/Pico.php | 8 +++++--- lib/PicoTwigExtension.php | 9 +++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index c8c514a..0d327af 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -1650,13 +1650,15 @@ class Pico * @see Pico::substituteFileContent() * @see Pico::getFileContent() * - * @param string $markdown Markdown contents of a page + * @param string $markdown Markdown contents of a page + * @param bool $singleLine whether to parse just a single line of markup * * @return string parsed contents (HTML) */ - public function parseFileContent($markdown) + public function parseFileContent($markdown, $singleLine = false) { - return $this->getParsedown()->text($markdown); + $markdownParser = $this->getParsedown(); + return !$singleLine ? $markdownParser->text($markdown) : $markdownParser->line($markdown); } /** diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index c0861f7..f61c2c9 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -111,15 +111,16 @@ class PicoTwigExtension extends Twig_Extension * @see Pico::substituteFileContent() * @see Pico::parseFileContent() * - * @param string $markdown markdown to parse - * @param array $meta meta data to use for %meta.*% replacement + * @param string $markdown markdown to parse + * @param array $meta meta data to use for %meta.*% replacement + * @param bool $singleLine whether to parse just a single line of markup * * @return string parsed HTML */ - public function markdownFilter($markdown, array $meta = array()) + public function markdownFilter($markdown, array $meta = array(), $singleLine = false) { $markdown = $this->getPico()->substituteFileContent($markdown, $meta); - return $this->getPico()->parseFileContent($markdown); + return $this->getPico()->parseFileContent($markdown, $singleLine); } /** From 2a23edde4e7016b6a3b21277f84bce8ccf12ac86 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 26 Oct 2019 11:34:58 +0200 Subject: [PATCH 47/73] Fix code formatting --- lib/Pico.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Pico.php b/lib/Pico.php index 0d327af..4cc7bc4 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2106,8 +2106,8 @@ class Pico if (isset($pages[$page])) { $pageData = &$pages[$page]; if (!isset($pageData['content'])) { - $pageData['content'] = $pico->prepareFileContent($pageData['raw_content'], $pageData['meta']); - $pageData['content'] = $pico->parseFileContent($pageData['content']); + $markdown = $pico->prepareFileContent($pageData['raw_content'], $pageData['meta']); + $pageData['content'] = $pico->parseFileContent($markdown); } return $pageData['content']; } From 3480a520d93ca499dc7fd4c93765a367d3e464ad Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 26 Oct 2019 14:00:45 +0200 Subject: [PATCH 48/73] Build system: Use PHP 5.3 to create pre-built release packages Otherwise Composer downloads a newer version of Twig which isn't compatible with PHP 5.3. Since we don't pin down specific versions of our dependencies, Composer-based installations might use newer versions of Twig which aren't compatible with PHP 5.3. Raising the PHP requirements requires a new major version, something that will definitly happen with Pico 3.0. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 255871d..5e5ecbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,8 @@ jobs: # Deployment stage - stage: deploy - php: 5.6 + php: 5.3 + dist: precise sudo: required install: - '[ "$TRAVIS_PULL_REQUEST" == "false" ] || travis_terminate 0' From e0415c8c1d244b0c6da6cfa6d3e5991e15fccbed Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 26 Oct 2019 14:02:11 +0200 Subject: [PATCH 49/73] Mark Pico's content Twig variable as being safe This no longer requires themes to output the `content` variable using `{{ content|raw }}`, theme developers can use `{{ content }}` as before. --- lib/Pico.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index 4cc7bc4..983e012 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -2141,7 +2141,7 @@ class Pico 'theme_url' => $this->getConfig('themes_url') . $this->getTheme(), 'site_title' => $this->getConfig('site_title'), 'meta' => $this->meta, - 'content' => $this->content, + 'content' => new Twig_Markup($this->content, 'UTF-8'), 'pages' => $this->pages, 'previous_page' => $this->previousPage, 'current_page' => $this->currentPage, From 7bbd8736d1234778b1e6da55a6a6076369c00f2b Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 26 Oct 2019 14:09:58 +0200 Subject: [PATCH 50/73] Update CHANGELOG.md --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e5e481..6765c73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,61 @@ Pico Changelog `PicoDeprecated`'s changelog. Please note that BC-breaking changes are only possible with a new major version. +### Version 2.1.0-beta.1 +Released: - + +``` +* [New] Add `assets_dir`, `assets_url` and `plugins_url` config params +* [New] Add `%config.*%` Markdown placeholders for scalar config params and the + `%assets_url%`, `%themes_url%` and `%plugins_url%` placeholders +* [New] Add `content-sample/theme.md` for theme testing purposes +* [New] Introduce API versioning for themes and support theme-specific configs + using the new `pico-theme.yml` in a theme's directory; `pico-theme.yml` + allows a theme to influence Pico's Twig config, to register known meta + headers and to provide defaults for theme config params +* [New] Add `assets_url`, `themes_url` and `plugins_url` Twig variables +* [New] Add `pages` Twig function to deal with Pico's page tree; this function + replaces the raw usage of Pico's `pages` array in themes +* [New] Add `url` Twig filter to replace URL placeholders (e.g. `%base_url%`) + in strings using the new `Pico::substituteUrl()` method +* [New] Add `onThemeLoading` and `onThemeLoaded` events +* [New] Add `debug` config param and the `Pico::isDebugModeEnabled()` method, + cehcking the `PICO_DEBUG` environment variable, to enable debugging +* [New] Add new `Pico::getNormalizedPath()` method to normalize a path; this + method should be used to prevent content dir breakouts when dealing + with paths provided by user input +* [New] Add new `Pico::getUrlFromPath()` method to guess a URL from a file path +* [New] Add new `Pico::getAbsoluteUrl()` method to make a relative URL absolute +* [New] #505: Create pre-built `.zip` release archives +* [Fixed] #461: Proberly handle content files with a UTF-8 BOM +* [Changed] Introduce API version 3 +* [Changed] Rename `theme_url` config param to `themes_url`; the `theme_url` + Twig variable and Markdown placeholder are kept unchanged +* [Changed] Update to Parsedown Extra 0.8 and Parsedown 1.8 (both still beta) +* [Changed] Enable Twig's `autoescape` feature by default; outputting a + variable now causes Twig to escape HTML markup; Pico's `content` + variable is a notable exception, as it is marked as being HTML safe +* [Changed] Rename `prev_page` Twig variable to `previous_page` +* [Changed] Mark `markdown` and `content` Twig filters as being HTML safe +* [Changed] Add `$singleLine` param to `markdown` Twig filter as well as the + `Pico::parseFileContent()` method to parse just a single line of + Markdown input +* [Changed] Add `AbstractPicoPlugin::configEnabled()` method to check whether + a plugin should be enabled or disabled based on Pico's config +* [Changed] Deprecate the use of `AbstractPicoPlugin::__call()`, use + `PicoPluginInterface::getPico()` instead +* [Changed] Update to Twig 1.36 as last version supporting PHP 5.3, use a + Composer-based installation to use a newer Twig version +* [Changed] Add `$basePath` and `$endSlash` params to `Pico::getAbsolutePath()` +* [Changed] Deprecate `Pico::getBaseThemeUrl()` +* [Changed] Replace various `file_exists` calls with proper `is_file` calls +* [Changed] Improve release & build process +* [Changed] Improve PHP class docs +* [Changed] Various small improvements +* [Removed] Remove superfluous `base_dir` and `theme_dir` Twig variables +* [Removed] Remove `PicoPluginInterface::__construct()` +``` + ### Version 2.0.5-beta.1 Released: 2019-01-03 From 58f615403edb5b779ca56d2047f9086c33566206 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 3 Nov 2019 20:03:48 +0100 Subject: [PATCH 51/73] Refactor Build system - Separate Travis branch deployment and release deployment stages (also makes `deploy.sh` obsolete) - Add `clean.sh` and `release.sh` scripts to allow users to create "release" packages locally - Use `setup/*.sh` scripts to check and install build dependencies (like PHP_CodeSniffer, phpDocumentor and cloc) - Use `create-release.sh` of `picocms/ci-tools` to create release archives - Streamline script usage Use the following to test Pico and to create a "release" package locally: ```sh cd ~/My-Pico-Workspace/Components/pico ln -rs ../ci-tools .build/ci-tools . ./.build/ci-tools/init/local.sh.inc . ./.build/init.sh.inc phpcs --standard=.phpcs.xml "$PICO_PROJECT_DIR" clean.sh release.sh ``` --- .build/clean.sh | 25 +++++++++++ .build/create-release.sh | 97 ---------------------------------------- .build/deploy-branch.sh | 2 +- .build/deploy-release.sh | 5 ++- .build/deploy.sh | 6 --- .build/init.sh.inc | 19 ++++++++ .build/install.sh | 56 ++++------------------- .build/release.sh | 86 +++++++++++++++++++++++++++++++++++ .gitattributes | 2 +- .gitignore | 11 +++-- .travis.yml | 44 +++++++++--------- 11 files changed, 176 insertions(+), 177 deletions(-) create mode 100755 .build/clean.sh delete mode 100755 .build/create-release.sh delete mode 100755 .build/deploy.sh create mode 100644 .build/init.sh.inc create mode 100755 .build/release.sh diff --git a/.build/clean.sh b/.build/clean.sh new file mode 100755 index 0000000..a816cb8 --- /dev/null +++ b/.build/clean.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -e + +[ -n "$PICO_BUILD_ENV" ] || { echo "No Pico build environment specified" >&2; exit 1; } + +# parameters +ARCHIVE_DIR="${1:-$PICO_PROJECT_DIR}" # directory to create release archives in + +# print parameters +echo "Cleaning up build environment..." +printf 'PICO_DEPLOY_DIR="%s"\n' "$PICO_DEPLOY_DIR" +printf 'PICO_BUILD_DIR="%s"\n' "$PICO_BUILD_DIR" +printf 'ARCHIVE_DIR="%s"\n' "$ARCHIVE_DIR" +echo + +echo "Removing deployment directory..." +[ ! -d "$PICO_DEPLOY_DIR" ] || rm -rf "$PICO_DEPLOY_DIR" + +echo "Removing build directory..." +[ ! -d "$PICO_BUILD_DIR" ] || rm -rf "$PICO_BUILD_DIR" + +echo "Removing release archives..." +find "$ARCHIVE_DIR" -mindepth 1 -maxdepth 1 \ + \( -name 'pico-release-*.tar.gz' -o -name 'pico-release-*.zip' \) \ + -delete diff --git a/.build/create-release.sh b/.build/create-release.sh deleted file mode 100755 index 8a799a2..0000000 --- a/.build/create-release.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env bash -set -e - -export PATH="$PICO_TOOLS_DIR:$PATH" -. "$PICO_TOOLS_DIR/functions/parse-version.sh.inc" - -# parameters -ARCHIVE_DIR="$1" # directory to create release archives in -ARCHIVE_FILENAME="$2" # release archive file name (without file extension) - -if [ -z "$ARCHIVE_DIR" ] || [ "$(realpath "$ARCHIVE_DIR")" == "$(realpath "$PICO_BUILD_DIR")" ]; then - echo "Unable to create release archives: Invalid release archive target dir '$ARCHIVE_DIR'" >&2 - exit 1 -fi -if [ -z "$ARCHIVE_FILENAME" ]; then - echo "Unable to create release archives: No release archive file name given" >&2 - exit 1 -fi - -# parse version -if ! parse_version "$PROJECT_REPO_TAG"; then - echo "Unable to create release archive: Invalid version '$PROJECT_REPO_TAG'" >&2 - exit 1 -fi - -# clone repo -github-clone.sh "$PICO_BUILD_DIR" "https://github.com/$RELEASE_REPO_SLUG.git" "$RELEASE_REPO_BRANCH" - -cd "$PICO_BUILD_DIR" - -# force Pico version -echo "Updating composer dependencies..." -composer require --no-update \ - "picocms/pico $VERSION_FULL@$VERSION_STABILITY" \ - "picocms/pico-theme $VERSION_FULL@$VERSION_STABILITY" \ - "picocms/pico-deprecated $VERSION_FULL@$VERSION_STABILITY" -echo - -# set minimum stability -if [ "$VERSION_STABILITY" != "stable" ]; then - echo "Setting minimum stability to '$VERSION_STABILITY'..." - composer config "minimum-stability" "$VERSION_STABILITY" - composer config "prefer-stable" "true" - echo -fi - -# install dependencies -echo "Running \`composer install\`..." -composer install --no-suggest --prefer-dist --no-dev --optimize-autoloader -echo - -# prepare release -echo "Replacing 'index.php'..." -cp vendor/picocms/pico/index.php.dist index.php - -echo "Adding 'README.md', 'CONTRIBUTING.md', 'CHANGELOG.md'..." -cp vendor/picocms/pico/README.md README.md -cp vendor/picocms/pico/CONTRIBUTING.md CONTRIBUTING.md -cp vendor/picocms/pico/CHANGELOG.md CHANGELOG.md - -echo "Preparing 'composer.json' for release..." -composer require --no-update \ - "picocms/pico ^$VERSION_MILESTONE" \ - "picocms/pico-theme ^$VERSION_MILESTONE" \ - "picocms/pico-deprecated ^$VERSION_MILESTONE" - -echo "Removing '.git' directory..." -rm -rf .git - -echo "Removing '.git' directories of dependencies..." -find vendor/ -type d -path 'vendor/*/*/.git' -print0 | xargs -0 rm -rf -find themes/ -type d -path 'themes/*/.git' -print0 | xargs -0 rm -rf -find plugins/ -type d -path 'plugins/*/.git' -print0 | xargs -0 rm -rf - -echo - -# create release archives -echo "Creating release archive '$ARCHIVE.tar.gz'..." - -if [ -e "$ARCHIVE_DIR/$ARCHIVE.tar.gz" ]; then - echo "Unable to create release archive: File '$ARCHIVE.tar.gz' exists" >&2 - exit 1 -fi - -find . -mindepth 1 -maxdepth 1 -printf '%f\0' \ - | xargs -0 -- tar -czf "$ARCHIVE_DIR/$ARCHIVE.tar.gz" -- -echo - -echo "Creating release archive '$ARCHIVE.zip'..." - -if [ -e "$ARCHIVE_DIR/$ARCHIVE.zip" ]; then - echo "Unable to create release archive: File '$ARCHIVE.zip' exists" >&2 - exit 1 -fi - -zip -q -r "$ARCHIVE_DIR/$ARCHIVE.zip" . -echo diff --git a/.build/deploy-branch.sh b/.build/deploy-branch.sh index 3917aa1..d8284a7 100755 --- a/.build/deploy-branch.sh +++ b/.build/deploy-branch.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -export PATH="$PICO_TOOLS_DIR:$PATH" +[ -n "$PICO_BUILD_ENV" ] || { echo "No Pico build environment specified" >&2; exit 1; } # get current Pico milestone VERSION="$(php -r "require_once('$PICO_PROJECT_DIR/lib/Pico.php'); echo Pico::VERSION;")" diff --git a/.build/deploy-release.sh b/.build/deploy-release.sh index c105d19..eef5d70 100755 --- a/.build/deploy-release.sh +++ b/.build/deploy-release.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -e +[ -n "$PICO_BUILD_ENV" ] || { echo "No Pico build environment specified" >&2; exit 1; } + DEPLOY_FULL="true" if [ "$DEPLOY_PHPDOC_RELEASES" != "true" ]; then echo "Skipping phpDoc release deployment because it has been disabled" @@ -31,10 +33,9 @@ if [ "$DEPLOY_FULL" != "true" ]; then echo fi -export PATH="$PICO_TOOLS_DIR:$PATH" +# parse version . "$PICO_TOOLS_DIR/functions/parse-version.sh.inc" -# parse version if ! parse_version "$PROJECT_REPO_TAG"; then echo "Invalid version '$PROJECT_REPO_TAG'; aborting..." >&2 exit 1 diff --git a/.build/deploy.sh b/.build/deploy.sh deleted file mode 100755 index 0ef0c10..0000000 --- a/.build/deploy.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$PROJECT_REPO_TAG" ]; then - exec "$(dirname "$0")/deploy-release.sh" -else - exec "$(dirname "$0")/deploy-branch.sh" -fi diff --git a/.build/init.sh.inc b/.build/init.sh.inc new file mode 100644 index 0000000..e9cee44 --- /dev/null +++ b/.build/init.sh.inc @@ -0,0 +1,19 @@ +if [ -z "$PICO_BUILD_ENV" ]; then + echo "No Pico build environment specified" >&2 + exit 1 +fi + +# add project build dir to $PATH +export PATH="$PICO_PROJECT_DIR/.build:$PATH" + +# set environment variables +__picocms_cmd export RELEASE_REPO_SLUG="${RELEASE_REPO_SLUG:-picocms/pico-composer}" +__picocms_cmd export RELEASE_REPO_BRANCH="${RELEASE_REPO_BRANCH:-master}" + +if [ "$PROJECT_REPO_SLUG" != "picocms/Pico" ]; then + __picocms_cmd export DEPLOY_REPO_SLUG="${DEPLOY_REPO_SLUG:-$PROJECT_REPO_SLUG}" + __picocms_cmd export DEPLOY_REPO_BRANCH="${DEPLOY_REPO_BRANCH:-gh-pages}" +else + __picocms_cmd export DEPLOY_REPO_SLUG="${DEPLOY_REPO_SLUG:-picocms.github.io}" + __picocms_cmd export DEPLOY_REPO_BRANCH="${DEPLOY_REPO_BRANCH:-master}" +fi diff --git a/.build/install.sh b/.build/install.sh index 9e047f8..dff079c 100755 --- a/.build/install.sh +++ b/.build/install.sh @@ -1,55 +1,17 @@ #!/usr/bin/env bash set -e +[ -n "$PICO_BUILD_ENV" ] || { echo "No Pico build environment specified" >&2; exit 1; } + # setup build system -echo "Installing build dependencies..." -echo - -case "$1" in - "--deploy") - echo "Synchronizing package index files..." - sudo apt-get update - echo - - echo "Installing cloc..." - sudo apt-get install -y cloc - echo - - echo "Installing phpDocumentor..." - curl --location --output "$PICO_TOOLS_DIR/phpdoc" \ - "https://github.com/phpDocumentor/phpDocumentor2/releases/latest/download/phpDocumentor.phar" - chmod +x "$PICO_TOOLS_DIR/phpdoc" - echo - ;; -esac - -echo "Installing PHP_CodeSniffer..." -if [ "$(php -r 'echo PHP_VERSION_ID;')" -ge 50400 ]; then - PHPCS_DOWNLOAD="https://github.com/squizlabs/PHP_CodeSniffer/releases/latest/download/" -else - PHPCS_DOWNLOAD="https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.9.2/" -fi - -curl --location --output "$PICO_TOOLS_DIR/phpcs" \ - "$PHPCS_DOWNLOAD/phpcs.phar" -chmod +x "$PICO_TOOLS_DIR/phpcs" - -curl --location --output "$PICO_TOOLS_DIR/phpcbf" \ - "$PHPCS_DOWNLOAD/phpcbf.phar" -chmod +x "$PICO_TOOLS_DIR/phpcbf" - -echo - -# setup composer -echo "Setup Composer..." - -# let composer use our GITHUB_OAUTH_TOKEN -if [ -n "$GITHUB_OAUTH_TOKEN" ]; then - composer config --global github-oauth.github.com "$GITHUB_OAUTH_TOKEN" -fi +BUILD_REQUIREMENTS=( --phpcs ) +[ "$1" != "--deploy" ] || BUILD_REQUIREMENTS+=( --cloc --phpdoc ) +"$PICO_TOOLS_DIR/setup/$PICO_BUILD_ENV.sh" "${BUILD_REQUIREMENTS[@]}" # set COMPOSER_ROOT_VERSION when necessary if [ -z "$COMPOSER_ROOT_VERSION" ] && [ -n "$PROJECT_REPO_BRANCH" ]; then + echo "Setting up Composer..." + PICO_VERSION_PATTERN="$(php -r " \$json = json_decode(file_get_contents('$PICO_PROJECT_DIR/composer.json'), true); if (\$json !== null) { @@ -69,9 +31,9 @@ if [ -z "$COMPOSER_ROOT_VERSION" ] && [ -n "$PROJECT_REPO_BRANCH" ]; then if [ -n "$PICO_VERSION_PATTERN" ]; then export COMPOSER_ROOT_VERSION="$PICO_VERSION_PATTERN" fi -fi -echo + echo +fi # install dependencies echo "Running \`composer install\`$([ -n "$COMPOSER_ROOT_VERSION" ] && echo -n " ($COMPOSER_ROOT_VERSION)")..." diff --git a/.build/release.sh b/.build/release.sh new file mode 100755 index 0000000..9623b3f --- /dev/null +++ b/.build/release.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +set -e + +[ -n "$PICO_BUILD_ENV" ] || { echo "No Pico build environment specified" >&2; exit 1; } + +# parameters +VERSION="${1:-$PROJECT_REPO_TAG}" # version to create a release for +ARCHIVE_DIR="${2:-$PICO_PROJECT_DIR}" # directory to create release archives in + +# print parameters +echo "Creating new release..." +printf 'VERSION="%s"\n' "$VERSION" +echo + +# guess version string +if [ -z "$VERSION" ]; then + PICO_VERSION="$(php -r " + require_once('$PICO_PROJECT_DIR/lib/Pico.php'); + echo preg_replace('/-(?:dev|n|nightly)(?:[.-]?[0-9]+)?(?:[.-]dev)?$/', '', Pico::VERSION); + ")" + + VERSION="v$PICO_VERSION-dev+${PROJECT_REPO_BRANCH:-master}" + echo "Creating development release of Pico v$PICO_VERSION ($VERSION)..." + echo +fi + +# parse version +. "$PICO_TOOLS_DIR/functions/parse-version.sh.inc" + +if ! parse_version "$VERSION"; then + echo "Unable to create release archive: Invalid version '$VERSION'" >&2 + exit 1 +fi + +DEPENDENCY_VERSION="$VERSION_FULL@$VERSION_STABILITY" +if [ "$VERSION_STABILITY" == "dev" ] && [ -n "$VERSION_BUILD" ]; then + DEPENDENCY_VERSION="dev-$VERSION_BUILD" +fi + +# clone repo +github-clone.sh "$PICO_BUILD_DIR" "https://github.com/$RELEASE_REPO_SLUG.git" "$RELEASE_REPO_BRANCH" + +cd "$PICO_BUILD_DIR" + +# force Pico version +echo "Updating composer dependencies..." +composer require --no-update \ + "picocms/pico $DEPENDENCY_VERSION" \ + "picocms/pico-theme $DEPENDENCY_VERSION" \ + "picocms/pico-deprecated $DEPENDENCY_VERSION" +echo + +# set minimum stability +if [ "$VERSION_STABILITY" != "stable" ]; then + echo "Setting minimum stability to '$VERSION_STABILITY'..." + composer config "minimum-stability" "$VERSION_STABILITY" + composer config "prefer-stable" "true" + echo +fi + +# install dependencies +echo "Running \`composer install\`..." +composer install --no-suggest --prefer-dist --no-dev --optimize-autoloader +echo + +# prepare release +echo "Replacing 'index.php'..." +cp vendor/picocms/pico/index.php.dist index.php + +echo "Adding 'README.md', 'CONTRIBUTING.md', 'CHANGELOG.md'..." +cp vendor/picocms/pico/README.md README.md +cp vendor/picocms/pico/CONTRIBUTING.md CONTRIBUTING.md +cp vendor/picocms/pico/CHANGELOG.md CHANGELOG.md + +echo "Removing '.git' directories of plugins and themes..." +find themes/ -type d -path 'themes/*/.git' -print0 | xargs -0 rm -rf +find plugins/ -type d -path 'plugins/*/.git' -print0 | xargs -0 rm -rf + +echo "Preparing 'composer.json' for release..." +composer require --no-update \ + "picocms/pico ^$VERSION_MILESTONE" \ + "picocms/pico-theme ^$VERSION_MILESTONE" \ + "picocms/pico-deprecated ^$VERSION_MILESTONE" + +# create release archives +create-release.sh "$PICO_BUILD_DIR" "$ARCHIVE_DIR" "pico-release-v$VERSION_FULL" diff --git a/.gitattributes b/.gitattributes index f03d557..bc9dbf2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ -/.github export-ignore /.build export-ignore +/.github export-ignore /assets/.gitignore export-ignore /config/.gitignore export-ignore /content/.gitignore export-ignore diff --git a/.gitignore b/.gitignore index bccec62..490151f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,12 +10,17 @@ desktop.ini .DS_Store ._* -# composer +# Composer /composer.lock -/composer.phar /vendor +# Build system +/.build/build +/.build/deploy +/.build/ci-tools +/pico-release-*.tar.gz +/pico-release-*.zip + # phpDocumentor /.build/phpdoc /.build/phpdoc.cache -/phpDocumentor.phar diff --git a/.travis.yml b/.travis.yml index 5e5ecbc..6c36396 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,48 +23,52 @@ jobs: - php: hhvm-3.27 # until Sep 2019 - php: hhvm-3.30 # until Nov 2019 - # Deployment stage - - stage: deploy - php: 5.3 - dist: precise + # Branch deployment stage + - stage: deploy-branch + if: type == "push" && tag IS blank + php: 5.6 sudo: required install: - - '[ "$TRAVIS_PULL_REQUEST" == "false" ] || travis_terminate 0' - - '[ -n "$TRAVIS_TAG" ] || [[ ",$DEPLOY_PHPDOC_BRANCHES," == *,"$TRAVIS_BRANCH",* ]] || travis_terminate 0' + - '[[ ",$DEPLOY_PHPDOC_BRANCHES," == *,"$TRAVIS_BRANCH",* ]] || travis_terminate 0' - install.sh --deploy script: - - deploy.sh + - deploy-branch.sh + + # Release deployment stage + - stage: deploy-release + if: tag IS present + php: 5.3 + dist: precise + install: + - install.sh --deploy + script: + - '[ "$PROJECT_REPO_TAG" == "v$(php -r "require_once(\"lib/Pico.php\"); echo Pico::VERSION;")" ]' + - deploy-release.sh before_deploy: - - '[ "$TRAVIS_TAG" == "v$(php -r "require_once(\"lib/Pico.php\"); echo Pico::VERSION;")" ]' - - create-release.sh "$TRAVIS_BUILD_DIR" "pico-release-$TRAVIS_TAG" + - release.sh deploy: provider: releases api_key: ${GITHUB_OAUTH_TOKEN} file: - - pico-release-$TRAVIS_TAG.tar.gz - - pico-release-$TRAVIS_TAG.zip + - pico-release-$PROJECT_REPO_TAG.tar.gz + - pico-release-$PROJECT_REPO_TAG.zip skip_cleanup: true - name: Version ${TRAVIS_TAG:1} + name: Version ${PROJECT_REPO_TAG:1} draft: true - on: - tags: true # Ignore nightly build failures allow_failures: - php: nightly - fast-finish: true + fast_finish: true before_install: - - export PATH="$TRAVIS_BUILD_DIR/.build:$PATH" - export PICO_TOOLS_DIR="$HOME/__picocms_tools" - git clone --branch="$TOOLS_REPO_BRANCH" "https://github.com/$TOOLS_REPO_SLUG.git" "$PICO_TOOLS_DIR" - . "$PICO_TOOLS_DIR/init/travis.sh.inc" + - . "$PICO_PROJECT_DIR/.build/init.sh.inc" install: - install.sh -before_script: - - export PATH="$TRAVIS_BUILD_DIR/vendor/bin:$PATH" - script: - - phpcs --standard=.phpcs.xml "$TRAVIS_BUILD_DIR" + - phpcs --standard=.phpcs.xml "$PICO_PROJECT_DIR" From dec44817a5665b230f214540a23804dcf2dd18b3 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 3 Nov 2019 21:18:28 +0100 Subject: [PATCH 52/73] Build system: Add $PROJECT_REPO_TAG param to release.sh call --- .travis.yml | 2 +- CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6c36396..a129285 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,7 @@ jobs: - '[ "$PROJECT_REPO_TAG" == "v$(php -r "require_once(\"lib/Pico.php\"); echo Pico::VERSION;")" ]' - deploy-release.sh before_deploy: - - release.sh + - release.sh "$PROJECT_REPO_TAG" deploy: provider: releases api_key: ${GITHUB_OAUTH_TOKEN} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6765c73..99ff08a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,7 @@ Released: - * [Changed] Add `$basePath` and `$endSlash` params to `Pico::getAbsolutePath()` * [Changed] Deprecate `Pico::getBaseThemeUrl()` * [Changed] Replace various `file_exists` calls with proper `is_file` calls -* [Changed] Improve release & build process +* [Changed] Refactor release & build system * [Changed] Improve PHP class docs * [Changed] Various small improvements * [Removed] Remove superfluous `base_dir` and `theme_dir` Twig variables From 03d475c31c8a6ea3e676c34d93a7908133b4be65 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 4 Nov 2019 00:36:05 +0100 Subject: [PATCH 53/73] Update Pico's sample contents to reflect the changes of Pico 2.1 --- content-sample/_meta.md | 2 +- content-sample/index.md | 225 ++++++++++++++++++++++++---------------- 2 files changed, 139 insertions(+), 88 deletions(-) diff --git a/content-sample/_meta.md b/content-sample/_meta.md index 9cdf202..5a17b8c 100644 --- a/content-sample/_meta.md +++ b/content-sample/_meta.md @@ -1,5 +1,5 @@ --- -social: +Social: - title: Visit us on GitHub url: https://github.com/picocms/Pico icon: octocat diff --git a/content-sample/index.md b/content-sample/index.md index 0a702f7..a6b3389 100644 --- a/content-sample/index.md +++ b/content-sample/index.md @@ -85,10 +85,9 @@ page instead. As a common practice, we recommend you to separate your contents and assets (like images, downloads, etc.). We even deny access to your `content` directory by default. If you want to use some assets (e.g. a image) in one of your content -files, you should create an `assets` folder in Pico's root directory and upload -your assets there. You can then access them in your Markdown using -%base_url%/assets/ for example: -!\[Image Title\](%base_url%/assets/image.png) +files, use Pico's `assets` folder. You can then access them in your Markdown +using the %assets_url% placeholder, for example: +!\[Image Title\](%assets_url%/assets/image.png) ### Text File Markup @@ -121,7 +120,7 @@ classes to your theme. For example, you might want to add some CSS classes to your theme to rule how much of the available space a image should use (e.g. `img.small { width: 80%; }`). You can then use these CSS classes in your Markdown files, for example: -!\[Image Title\](%base_url%/assets/image.png) {.small} +!\[Image Title\](%assets_url%/image.png) {.small} There are also certain variables that you can use in your text files: @@ -129,9 +128,15 @@ There are also certain variables that you can use in your text files: * %base_url% - The URL to your Pico site; internal links can be specified using %base_url%?sub/page * %theme_url% - The URL to the currently used theme +* %assets_url% - The URL to Pico's `assets` directory +* %themes_url% - The URL to Pico's `themes` directory; + don't confuse this with %theme_url% +* %plugins_url% - The URL to Pico's `plugins` directory * %version% - Pico's current version string (e.g. `2.0.0`) * %meta.*% - Access any meta variable of the current page, e.g. %meta.author% is replaced with `Joe Bloggs` +* %config.*% - Access any scalar config variable, + e.g. %config.theme% is replaced with `default` ### Blogging @@ -154,14 +159,12 @@ something like the following: `index.twig`), it will create a list of all your blog articles. Add the following Twig snippet to `blog-index.twig` near `{{ content }}`: ``` - {% for page in pages|sort_by("time")|reverse %} - {% if page.id starts with "blog/" and not page.hidden %} -
-

{{ page.title }}

-

{{ page.date_formatted }}

-

{{ page.description }}

-
- {% endif %} + {% for page in pages("blog")|sort_by("time")|reverse if not page.hidden %} +
+

{{ page.title }}

+

{{ page.date_formatted }}

+

{{ page.description }}

+
{% endfor %} ``` @@ -179,75 +182,58 @@ details. ### Themes -You can create themes for your Pico installation in the `themes` folder. Check -out the default theme for an example. Pico uses [Twig][] for template -rendering. You can select your theme by setting the `theme` option in -`config/config.yml` to the name of your theme folder. +You can create themes for your Pico installation in the `themes` folder. Pico +uses [Twig][] for template rendering. You can select your theme by setting the +`theme` option in `config/config.yml` to the name of your theme folder. + +[Pico's default theme][PicoTheme] isn't really intended to be used for a +productive website, it's rather a starting point for creating your own theme. +If the default theme isn't sufficient for you, and you don't want to create +your own theme, you can use one of the great themes third-party developers and +designers created in the past. As with plugins, you can find themes in +[our Wiki][WikiThemes] and on [our website][OfficialThemes]. All themes must include an `index.twig` file to define the HTML structure of -the theme. Below are the Twig variables that are available to use in your -theme. Please note that paths (e.g. `{{ base_dir }}`) and URLs -(e.g. `{{ base_url }}`) don't have a trailing slash. +the theme, and a `pico-theme.yml` to set the necessary config parameters. Just +refer to Pico's default theme as an example. You can use different templates +for different content files by specifying the `Template` meta header. Simply +add e.g. `Template: blog` to the YAML header of a content file and Pico will +use the `blog.twig` template in your theme folder to display the page. + +Below are the Twig variables that are available to use in themes. Please note +that URLs (e.g. `{{ base_url }}`) never include a trailing slash. * `{{ site_title }}` - Shortcut to the site title (see `config/config.yml`) * `{{ config }}` - Contains the values you set in `config/config.yml` (e.g. `{{ config.theme }}` becomes `default`) -* `{{ base_dir }}` - The path to your Pico root directory * `{{ base_url }}` - The URL to your Pico site; use Twig's `link` filter to specify internal links (e.g. `{{ "sub/page"|link }}`), this guarantees that your link works whether URL rewriting is enabled or not -* `{{ theme_dir }}` - The path to the currently active theme * `{{ theme_url }}` - The URL to the currently active theme -* `{{ version }}` - Pico's current version string (e.g. `2.0.0`) +* `{{ assets_url }}` - The URL to Pico's `assets` directory +* `{{ themes_url }}` - The URL to Pico's `themes` directory; don't confuse this + with `{{ theme_url }}` +* `{{ plugins_url }}` - The URL to Pico's `plugins` directory +* `{{ version }}` - Pico's current version string (e.g. `%version%`) * `{{ meta }}` - Contains the meta values of the current page - * `{{ meta.title }}` - * `{{ meta.description }}` - * `{{ meta.author }}` - * `{{ meta.date }}` - * `{{ meta.date_formatted }}` - * `{{ meta.time }}` - * `{{ meta.robots }}` + * `{{ meta.title }}` - The `Title` YAML header + * `{{ meta.description }}` - The `Description` YAML header + * `{{ meta.author }}` - The `Author` YAML header + * `{{ meta.date }}` - The `Date` YAML header + * `{{ meta.date_formatted }}` - The formatted date of the page as specified + by the `date_format` parameter in your + `config/config.yml` + * `{{ meta.time }}` - The [Unix timestamp][UnixTimestamp] derived from the + `Date` YAML header + * `{{ meta.robots }}` - The `Robots` YAML header * ... * `{{ content }}` - The content of the current page after it has been processed through Markdown -* `{{ pages }}` - A collection of all the content pages in your site - * `{{ page.id }}` - The relative path to the content file (unique ID) - * `{{ page.url }}` - The URL to the page - * `{{ page.title }}` - The title of the page (YAML header) - * `{{ page.description }}` - The description of the page (YAML header) - * `{{ page.author }}` - The author of the page (YAML header) - * `{{ page.time }}` - The [Unix timestamp][UnixTimestamp] derived from - the `Date` header - * `{{ page.date }}` - The date of the page (YAML header) - * `{{ page.date_formatted }}` - The formatted date of the page as specified - by the `date_format` parameter in your - `config/config.yml` - * `{{ page.raw_content }}` - The raw, not yet parsed contents of the page; - use Twig's `content` filter to get the parsed - contents of a page by passing its unique ID - (e.g. `{{ "sub/page"|content }}`) - * `{{ page.meta }}`- The meta values of the page (see `{{ meta }}` above) - * `{{ page.previous_page }}` - The data of the respective previous page - * `{{ page.next_page }}` - The data of the respective next page - * `{{ page.tree_node }}` - The page's node in Pico's page tree -* `{{ prev_page }}` - The data of the previous page (relative to `current_page`) -* `{{ current_page }}` - The data of the current page (see `{{ pages }}` above) -* `{{ next_page }}` - The data of the next page (relative to `current_page`) - -Pages can be used like the following: - - - -Besides using the `{{ pages }}` list, you can also access pages using Pico's -page tree. The page tree allows you to iterate through Pico's pages using a tree -structure, so you can e.g. iterate just a page's direct children. It allows you -to build recursive menus (like dropdowns) and to filter pages more easily. Just -head over to Pico's [page tree documentation][FeaturesPageTree] for details. +* `{{ prev_page }}` - The data of the previous page, relative to `current_page` +* `{{ current_page }}` - The data of the current page; refer to the "Pages" + section below for details +* `{{ next_page }}` - The data of the next page, relative to `current_page` To call assets from your theme, use `{{ theme_url }}`. For instance, to include the CSS file `themes/my_theme/example.css`, add @@ -255,19 +241,94 @@ the CSS file `themes/my_theme/example.css`, add to your `index.twig`. This works for arbitrary files in your theme's folder, including images and JavaScript files. -Additional to Twigs extensive list of filters, functions and tags, Pico also -provides some useful additional filters to make theming easier. +Please note that Twig escapes HTML in all strings before outputting them. So +for example, if you add `headline: My favorite color` to the +YAML header of your page and output it using `{{ meta.headline }}`, you'll end +up seeing `My favorite color` - yes, including the markup! To +actually get it parsed, you must use `{{ meta.headline|raw }}` (resulting in +the expected My **favorite** color). Notable exceptions to this +are Pico's `content` variable (e.g. `{{ content }}`), Pico's `content` filter +(e.g. `{{ "sub/page"|content }}`), and Pico's `markdown` filter, they all are +marked as HTML safe. + +#### Dealing with pages + +There are several ways to access Pico's pages list. You can access the current +page's data using the `current_page` variable, or use the `prev_page` and/or +`next_page` variables to access the respective previous/next page in Pico's +pages list. But more importantly there's the `pages` function. No matter how +you access a page, it will always consist of the following data: + +* `{{ id }}` - The relative path to the content file (unique ID) +* `{{ url }}` - The URL to the page +* `{{ title }}` - The title of the page (`Title` YAML header) +* `{{ description }}` - The description of the page (`Description` YAML header) +* `{{ author }}` - The author of the page (`Author` YAML header) +* `{{ date }}` - The date of the page (`Date` YAML header) +* `{{ date_formatted }}` - The formatted date of the page as specified by the + `date_format` parameter in your `config/config.yml` +* `{{ time }}` - The [Unix timestamp][UnixTimestamp] derived from the page's + date +* `{{ raw_content }}` - The raw, not yet parsed contents of the page; use the + filter to get the parsed contents of a page by passing + its unique ID (e.g. `{{ "sub/page"|content }}`) +* `{{ meta }}` - The meta values of the page (see global `{{ meta }}` above) +* `{{ prev_page }}` - The data of the respective previous page +* `{{ next_page }}` - The data of the respective next page +* `{{ tree_node }}` - The page's node in Pico's page tree; check out Pico's + [page tree documentation][FeaturesPageTree] for details + +Pico's `pages()` function is the best way to access all of your site's pages. +It uses Pico's page tree to easily traverse a subset of Pico's pages list. It +allows you to filter pages and to build recursive menus (like dropdowns). By +default, `pages()` returns a list of all main pages (e.g. `content/page.md` and +`content/sub/index.md`, but not `content/sub/page.md` or `content/index.md`). +If you want to return all pages below a specific folder (e.g. `content/blog/`), +pass the folder name as first parameter to the function (e.g. `pages("blog")`). +Naturally you can also pass variables to the function. For example, to return a +list of all child pages of the current page, use `pages(current_page.id)`. +Check out the following code snippet: + +
+ {% for page in pages(current_page.id) if not page.hidden %} + + {% endfor %} +
+ +The `pages()` function is very powerful and also allows you to return not just +a page's child pages by passing the `depth`, `depthOffset` and `offset` params. +For example, if you pass `pages(depthOffset=-1)`, the list will also include +Pico's main index page (i.e. `content/index.md`). This one is commonly used to +create a theme's main navigation. If you want to learn more, head over to +Pico's complete [`pages` function documentation][FeaturesPagesFunction]. + +#### Twig filters and functions + +Additional to [Twig][]'s extensive list of filters, functions and tags, Pico +also provides some useful additional filters and functions to make theming +even easier. * Pass the unique ID of a page to the `link` filter to return the page's URL (e.g. `{{ "sub/page"|link }}` gets `%base_url%?sub/page`). +* You can replace URL placeholders (like %base_url%) in + arbitrary strings using the `url` filter. This is helpful together with meta + variables, e.g. if you add image: %assets_url%/stock.jpg + to the YAML header of your page, `{{ meta.image|url }}` will return + `%assets_url%/stock.jpg`. * To get the parsed contents of a page, pass its unique ID to the `content` filter (e.g. `{{ "sub/page"|content }}`). -* You can parse any Markdown string using the `markdown` filter (e.g. you can - use Markdown in the `description` meta variable and later parse it in your - theme using `{{ meta.description|markdown }}`). You can pass meta data as - parameter to replace %meta.*% placeholders (e.g. - `{{ "Written *by %meta.author%*"|markdown(meta) }}` yields "Written by - *John Doe*"). +* You can parse any Markdown string using the `markdown` filter. For example, + you might use Markdown in the `description` meta variable and later parse it + in your theme using `{{ meta.description|markdown }}`. You can also pass meta + data as parameter to replace %meta.*% placeholders + (e.g. `{{ "Written by *%meta.author%*"|markdown(meta) }}` yields "Written by + *John Doe*"). However, please note that all contents will be wrapped inside + HTML paragraph elements (i.e. `

`). If you want to parse just a single + line of Markdown markup, pass the `singleLine` param to the `markdown` filter + (e.g. `{{ "This really is a *single* line"|markdown(singleLine=true) }}`). * Arrays can be sorted by one of its keys using the `sort_by` filter (e.g. `{% for page in pages|sort_by([ 'meta', 'nav' ]) %}...{% endfor %}` iterates through all pages, ordered by the `nav` meta header; please note the @@ -285,18 +346,6 @@ provides some useful additional filters to make theming easier. Twig! Simply head over to our [introductory page for accessing HTTP parameters][FeaturesHttpParams] for details. -You can use different templates for different content files by specifying the -`Template` meta header. Simply add e.g. `Template: blog` to the YAML header of -a content file and Pico will use the `blog.twig` template in your theme folder -to display the page. - -Pico's default theme isn't really intended to be used for a productive website, -it's rather a starting point for creating your own theme. If the default theme -isn't sufficient for you, and you don't want to create your own theme, you can -use one of the great themes third-party developers and designers created in the -past. As with plugins, you can find themes in [our Wiki][WikiThemes] and on -[our website][OfficialThemes]. - ### Plugins #### Plugins for users @@ -429,6 +478,7 @@ url.rewrite-if-not-file = ( For more help have a look at the Pico documentation at http://picocms.org/docs. [Pico]: http://picocms.org/ +[PicoTheme]: https://github.com/picocms/pico-theme [SampleContents]: https://github.com/picocms/Pico/tree/master/content-sample [Markdown]: http://daringfireball.net/projects/markdown/syntax [MarkdownExtra]: https://michelf.ca/projects/php-markdown/extra/ @@ -438,6 +488,7 @@ For more help have a look at the Pico documentation at http://picocms.org/docs. [Composer]: https://getcomposer.org/ [FeaturesHttpParams]: http://picocms.org/in-depth/features/http-params/ [FeaturesPageTree]: http://picocms.org/in-depth/features/page-tree/ +[FeaturesPagesFunction]: http://picocms.org/in-depth/features/pages-function/ [WikiThemes]: https://github.com/picocms/Pico/wiki/Pico-Themes [WikiPlugins]: https://github.com/picocms/Pico/wiki/Pico-Plugins [OfficialThemes]: http://picocms.org/themes/ From db1c6301e601cf16388eb4ba6d725aa5763862e7 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 4 Nov 2019 01:49:03 +0100 Subject: [PATCH 54/73] Build system: Fix release package deployment --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a129285..c96fb73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,8 @@ jobs: skip_cleanup: true name: Version ${PROJECT_REPO_TAG:1} draft: true + on: + tags: true # Ignore nightly build failures allow_failures: From 58ec760f04692403aac51d4539f98105ec38c9e3 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 4 Nov 2019 00:44:52 +0100 Subject: [PATCH 55/73] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ff08a..67f9c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Pico Changelog are only possible with a new major version. ### Version 2.1.0-beta.1 -Released: - +Released: 2019-11-03 ``` * [New] Add `assets_dir`, `assets_url` and `plugins_url` config params From 29f2e95160bdec5ffcacfd1b194c4f2b7a7aa0b9 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 4 Nov 2019 00:45:01 +0100 Subject: [PATCH 56/73] Update Pico::VERSION --- lib/Pico.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index 983e012..45961ae 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -49,7 +49,7 @@ class Pico * * @var string */ - const VERSION = '2.1.0-nightly'; + const VERSION = '2.1.0-beta.1'; /** * Pico version ID From 420ede0daab73af9c34c447880c13979cafd73b1 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 10 Nov 2019 17:24:25 +0100 Subject: [PATCH 58/73] composer.json: Remove 'ext-dom' extension Pico doesn't require 'ext-dom' itself, Parsedown Extra does; Parsedown Extra didn't declare this dependency before, but Parsedown Extra 0.8 finally does. --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 016b823..ea7a6f9 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,6 @@ }, "require": { "php": ">=5.3.6", - "ext-dom": "*", "twig/twig": "^1.36", "symfony/yaml" : "^2.8", "erusev/parsedown": "~1.8.0|1.8.0@beta", From 3b8d89fa30e7e422828362fef8692baa63aa054c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 11 Nov 2019 18:33:22 +0100 Subject: [PATCH 59/73] composer.json: Adding myself as lead dev --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index ea7a6f9..eae0d8d 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,11 @@ "email": "gilbert@pellegrom.me", "role": "Project Founder" }, + { + "name": "Daniel Rudolf", + "email": "picocms.org@daniel-rudolf.de", + "role": "Lead Developer" + }, { "name": "The Pico Community", "homepage": "http://picocms.org/" From c99f3cbbdf5c273c05012891785c65f1525127fd Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 11 Nov 2019 19:02:11 +0100 Subject: [PATCH 60/73] Update @version phpDoc class docs --- lib/AbstractPicoPlugin.php | 2 +- lib/PicoPluginInterface.php | 2 +- lib/PicoTwigExtension.php | 2 +- plugins/DummyPlugin.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/AbstractPicoPlugin.php b/lib/AbstractPicoPlugin.php index c586c29..b732ac2 100644 --- a/lib/AbstractPicoPlugin.php +++ b/lib/AbstractPicoPlugin.php @@ -21,7 +21,7 @@ * @author Daniel Rudolf * @link http://picocms.org * @license http://opensource.org/licenses/MIT The MIT License - * @version 2.0 + * @version 2.1 */ abstract class AbstractPicoPlugin implements PicoPluginInterface { diff --git a/lib/PicoPluginInterface.php b/lib/PicoPluginInterface.php index 6add326..85f9dfb 100644 --- a/lib/PicoPluginInterface.php +++ b/lib/PicoPluginInterface.php @@ -26,7 +26,7 @@ * @author Daniel Rudolf * @link http://picocms.org * @license http://opensource.org/licenses/MIT The MIT License - * @version 2.0 + * @version 2.1 */ interface PicoPluginInterface { diff --git a/lib/PicoTwigExtension.php b/lib/PicoTwigExtension.php index f61c2c9..0988110 100644 --- a/lib/PicoTwigExtension.php +++ b/lib/PicoTwigExtension.php @@ -16,7 +16,7 @@ * @author Daniel Rudolf * @link http://picocms.org * @license http://opensource.org/licenses/MIT The MIT License - * @version 2.0 + * @version 2.1 */ class PicoTwigExtension extends Twig_Extension { diff --git a/plugins/DummyPlugin.php b/plugins/DummyPlugin.php index b17d51c..415103b 100644 --- a/plugins/DummyPlugin.php +++ b/plugins/DummyPlugin.php @@ -19,7 +19,7 @@ * @author Daniel Rudolf * @link http://picocms.org * @license http://opensource.org/licenses/MIT The MIT License - * @version 2.0 + * @version 2.1 */ class DummyPlugin extends AbstractPicoPlugin { From 03d466c117c6a87616d0dfdc4520aee39c38c6e5 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Wed, 13 Nov 2019 00:23:43 +0100 Subject: [PATCH 61/73] Fix Pico's sample contents --- content-sample/index.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/content-sample/index.md b/content-sample/index.md index a6b3389..fa56be0 100644 --- a/content-sample/index.md +++ b/content-sample/index.md @@ -87,7 +87,7 @@ As a common practice, we recommend you to separate your contents and assets by default. If you want to use some assets (e.g. a image) in one of your content files, use Pico's `assets` folder. You can then access them in your Markdown using the %assets_url% placeholder, for example: -!\[Image Title\](%assets_url%/assets/image.png) +!\[Image Title\](%assets_url%/image.png) ### Text File Markup @@ -230,7 +230,8 @@ that URLs (e.g. `{{ base_url }}`) never include a trailing slash. * ... * `{{ content }}` - The content of the current page after it has been processed through Markdown -* `{{ prev_page }}` - The data of the previous page, relative to `current_page` +* `{{ previous_page }}` - The data of the previous page, relative to + `current_page` * `{{ current_page }}` - The data of the current page; refer to the "Pages" section below for details * `{{ next_page }}` - The data of the next page, relative to `current_page` From c02e3fa7a4a19ac62c2da419454d9471b7aab97e Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 18 Nov 2019 09:55:36 +0100 Subject: [PATCH 62/73] Various small improvements --- CHANGELOG.md | 9 +++++---- content-sample/index.md | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67f9c39..c79764f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Pico Changelog Released: 2019-11-03 ``` +* [New] Introduce API version 3 * [New] Add `assets_dir`, `assets_url` and `plugins_url` config params * [New] Add `%config.*%` Markdown placeholders for scalar config params and the `%assets_url%`, `%themes_url%` and `%plugins_url%` placeholders @@ -35,7 +36,7 @@ Released: 2019-11-03 in strings using the new `Pico::substituteUrl()` method * [New] Add `onThemeLoading` and `onThemeLoaded` events * [New] Add `debug` config param and the `Pico::isDebugModeEnabled()` method, - cehcking the `PICO_DEBUG` environment variable, to enable debugging + checking the `PICO_DEBUG` environment variable, to enable debugging * [New] Add new `Pico::getNormalizedPath()` method to normalize a path; this method should be used to prevent content dir breakouts when dealing with paths provided by user input @@ -43,7 +44,6 @@ Released: 2019-11-03 * [New] Add new `Pico::getAbsoluteUrl()` method to make a relative URL absolute * [New] #505: Create pre-built `.zip` release archives * [Fixed] #461: Proberly handle content files with a UTF-8 BOM -* [Changed] Introduce API version 3 * [Changed] Rename `theme_url` config param to `themes_url`; the `theme_url` Twig variable and Markdown placeholder are kept unchanged * [Changed] Update to Parsedown Extra 0.8 and Parsedown 1.8 (both still beta) @@ -51,7 +51,8 @@ Released: 2019-11-03 variable now causes Twig to escape HTML markup; Pico's `content` variable is a notable exception, as it is marked as being HTML safe * [Changed] Rename `prev_page` Twig variable to `previous_page` -* [Changed] Mark `markdown` and `content` Twig filters as being HTML safe +* [Changed] Mark `markdown` and `content` Twig filters as well as the `content` + variable as being HTML safe * [Changed] Add `$singleLine` param to `markdown` Twig filter as well as the `Pico::parseFileContent()` method to parse just a single line of Markdown input @@ -65,7 +66,7 @@ Released: 2019-11-03 * [Changed] Deprecate `Pico::getBaseThemeUrl()` * [Changed] Replace various `file_exists` calls with proper `is_file` calls * [Changed] Refactor release & build system -* [Changed] Improve PHP class docs +* [Changed] Improve Pico docs and PHP class docs * [Changed] Various small improvements * [Removed] Remove superfluous `base_dir` and `theme_dir` Twig variables * [Removed] Remove `PicoPluginInterface::__construct()` diff --git a/content-sample/index.md b/content-sample/index.md index fa56be0..182819d 100644 --- a/content-sample/index.md +++ b/content-sample/index.md @@ -244,8 +244,8 @@ including images and JavaScript files. Please note that Twig escapes HTML in all strings before outputting them. So for example, if you add `headline: My favorite color` to the -YAML header of your page and output it using `{{ meta.headline }}`, you'll end -up seeing `My favorite color` - yes, including the markup! To +YAML header of a page and output it using `{{ meta.headline }}`, you'll end up +seeing `My favorite color` - yes, including the markup! To actually get it parsed, you must use `{{ meta.headline|raw }}` (resulting in the expected My **favorite** color). Notable exceptions to this are Pico's `content` variable (e.g. `{{ content }}`), Pico's `content` filter @@ -257,7 +257,7 @@ marked as HTML safe. There are several ways to access Pico's pages list. You can access the current page's data using the `current_page` variable, or use the `prev_page` and/or `next_page` variables to access the respective previous/next page in Pico's -pages list. But more importantly there's the `pages` function. No matter how +pages list. But more importantly there's the `pages()` function. No matter how you access a page, it will always consist of the following data: * `{{ id }}` - The relative path to the content file (unique ID) @@ -304,7 +304,7 @@ a page's child pages by passing the `depth`, `depthOffset` and `offset` params. For example, if you pass `pages(depthOffset=-1)`, the list will also include Pico's main index page (i.e. `content/index.md`). This one is commonly used to create a theme's main navigation. If you want to learn more, head over to -Pico's complete [`pages` function documentation][FeaturesPagesFunction]. +Pico's complete [`pages()` function documentation][FeaturesPagesFunction]. #### Twig filters and functions @@ -317,7 +317,7 @@ even easier. * You can replace URL placeholders (like %base_url%) in arbitrary strings using the `url` filter. This is helpful together with meta variables, e.g. if you add image: %assets_url%/stock.jpg - to the YAML header of your page, `{{ meta.image|url }}` will return + to the YAML header of a page, `{{ meta.image|url }}` will return `%assets_url%/stock.jpg`. * To get the parsed contents of a page, pass its unique ID to the `content` filter (e.g. `{{ "sub/page"|content }}`). From cc6a478939ab19cc22a88699308aaf80ea41860e Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 18 Nov 2019 16:49:08 +0100 Subject: [PATCH 63/73] README: Mention requirement of PHP extensions 'dom' and 'mbstring' Resolves #517 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f5a2e0..62ebe2d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Install Installing Pico is dead simple - and done in seconds! If you have access to a shell on your server (i.e. SSH access), we recommend using [Composer][]. If not, use a pre-bundled release. If you don't know what "SSH access" is, head over to the pre-bundled release. 😇 -Pico requires PHP 5.3.6+ +Pico requires PHP 5.3.6+ and the PHP extensions `dom` and `mbstring` to be enabled. ### I want to use Composer From 558840643b609694457cb5ef4fa1d3bb0271ae3c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 23 Nov 2019 19:38:45 +0100 Subject: [PATCH 64/73] Add Pico's logo and tagline to the sample contents --- content-sample/_meta.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content-sample/_meta.md b/content-sample/_meta.md index 5a17b8c..d18f051 100644 --- a/content-sample/_meta.md +++ b/content-sample/_meta.md @@ -1,4 +1,6 @@ --- +Logo: %theme_url%/img/pico-white.svg +Tagline: Making the web easy. Social: - title: Visit us on GitHub url: https://github.com/picocms/Pico From 47d533982db6498984acc0c165e5bd3575a20cc7 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 23 Nov 2019 19:39:16 +0100 Subject: [PATCH 65/73] Replace Placeholder logo in content-sample/theme.md by Pico's logo Also shows the new image utility classes of Pico's default theme Co-Authored-By: type76 --- content-sample/theme.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/content-sample/theme.md b/content-sample/theme.md index 647193b..a11aaa1 100644 --- a/content-sample/theme.md +++ b/content-sample/theme.md @@ -13,10 +13,14 @@ Text **Lorem ipsum dolor sit amet,** consectetur adipisici elit, *sed eiusmod tempor* incidunt ut labore et dolore magna aliqua.[^1] ~~Ut enim ad minim veniam,~~ quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat.[^2] [Quis aute iure reprehenderit][Link] in voluptate velit esse cillum dolore eu fugiat nulla pariatur. `Excepteur` sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. -[![Placeholder Logo by logoipsum ](data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzcuNzggMTAwIj48dGl0bGU+bG9nby01PC90aXRsZT48cGF0aCBkPSJNNDYuMjMsNDEuMzloMlY1NS4zMWg2djJoLThabTE0LjIzLDBhNiw2LDAsMSwxLTYsNkE2LDYsMCwwLDEsNjAuNDYsNDEuMzlabTAsMTBhNCw0LDAsMSwwLTQtNEE0LDQsMCwwLDAsNjAuNDYsNTEuMzdabS00LDMuOTRoOHYyaC04Wm0yNS42LDJoLTJWNTUuOWE3LjY5LDcuNjksMCwwLDEtNC40LDEuMzksOCw4LDAsMSwxLDYtMTMuMjNsLTEuNDcsMS4zMWE2LDYsMCwxLDAtNC40NiwxMCw1Ljc3LDUuNzcsMCwwLDAsNC4zOC0ySDc0Ljcxdi0ybDcuMzcsMFptNy4zNi0xNS45YTYsNiwwLDEsMS02LDZBNiw2LDAsMCwxLDg5LjQ0LDQxLjM5Wm0wLDEwYTQsNCwwLDEsMC00LTRBNCw0LDAsMCwwLDg5LjQ0LDUxLjM3Wm0tNCwzLjk0aDh2MmgtOFpNOTcuNTUsNDEuMzloMnYxNS45aC0yWm00Ljc3LDAsMiwwYTEuNDcsMS40NywwLDAsMSwuMzEsMCw2LDYsMCwxLDEsMCwxMS45M2gtLjMxdjRoLTJabTIsMnY3LjkxaC4zMWE0LDQsMCwxLDAsMC03LjkzQTEuNDcsMS40NywwLDAsMCwxMDQuMzEsNDMuNDRabTE0LjQxLS44NS0xLjQ4LDEuMTdhMS45NCwxLjk0LDAsMCwwLTMuMzMsMS4xM1Y0NWMwLC44OC42MSwxLjQ3LDEuOCwxLjU1LDQuNTguMjgsNi42OCwyLjQzLDYuNjgsNS4yN1Y1MmE1LjksNS45LDAsMCwxLTEwLjE3LDMuNDJsMS41My0xLjE4YTMuNiwzLjYsMCwwLDAsMi44LDEuMTRBMy43NCwzLjc0LDAsMCwwLDEyMC40OCw1MnYtLjE0YzAtMi42Ny0yLjYyLTMuMTYtNC44OS0zLjM4LTIuMS0uMi0zLjYxLTEuNTEtMy42MS0zLjRWNDVhMy44NSwzLjg1LDAsMCwxLDMuOTMtMy41NkEzLjc0LDMuNzQsMCwwLDEsMTE4LjcyLDQyLjU5Wm0xMS41LDEyLjcyYzIuNDksMCw0LjQ2LTIuNjksNC40Ni02di04aDJsMCw4YzAsNC40LTIuODgsNy45NS02LjQyLDcuOTVzLTYuNDItMy41NS02LjQyLTcuOTV2LThoMnY4QzEyNS43Nyw1Mi42MiwxMjcuNzQsNTUuMzEsMTMwLjIyLDU1LjMxWm0yMS44MSwyaC0yVjQ3LjM2bC00LjYxLDYtNC42MS02djkuOTNoLTJWNDEuMzlsNi42LDguNjksNi42LTguNjlaIiBzdHlsZT0iZmlsbDojMzk0MTQ5Ii8+PHBhdGggZD0iTTM2LjYzLDM5YTUuNDQsNS40NCwwLDAsMC0xMC44OCwwVjU1LjQ1bDQuNDgsMy4yMXY3LjgyaDEuOTJWNTguNjZsNC40OC0zLjIxWm0tMS45Miw5LjItMi41NiwyLjU2di0yLjRsMi41Ni0yLjU2Wm0tNy0yLjQsMi41NiwyLjU2djIuNGwtMi41Ni0yLjU2Wm03LTIuNzItMy41MiwzLjUyTDI3LjY3LDQzdi0yLjRsMy41MiwzLjUyLDMuNTItMy41MlptLTMuNTItNy42YTMuNTQsMy41NCwwLDAsMSwzLjQsMi42MWwtMy40LDMuMzktMy40LTMuMzlBMy41NCwzLjU0LDAsMCwxLDMxLjE5LDM1LjQ0Wm0tMy41MiwxOVY1MC44OGwyLjU2LDIuNTZWNTYuM1ptNC40OCwxLjgzVjUzLjQ0bDIuNTYtMi41NnYzLjU5WiIgc3R5bGU9ImZpbGw6IzM5NDE0OSIvPjwvc3ZnPg==)](%base_url% "Placeholder Logo by logoipsum ") +[![Pico Logo](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDAiIGhlaWdodD0iNDAwIiB2aWV3Qm94PSIwIDAgNDAwIDQwMCI+PHBhdGggZD0ibTI5OC40IDE5NC43cTAtMTQuMTUtLjgtMzEuMmwtLjg1LTE0LjI1aC01MS4wNXY4OS45NWw4IDEuMXE5LjYgMS4wNSAxNy42IDEuMDUgNy45NSAwIDE3LjUtMS4wNSA0LjgtLjU1IDcuOTUtMS4xIDEuNjUtMjIuMiAxLjY1LTQ0LjVtLTY5Ljc1LTQ1LjhoLTQ5LjN2MTgyLjQ1bDcuNy44NXE5LjMuOCAxNyAuOCAxMi4zIDAgMjQuNi0xLjY1eiIgZmlsbD0iIzJlYWU5YiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTM4Ljg1IC00MC45NSkiLz48L3N2Zz4K)](%base_url% "Pico Logo") {.image .small .float-right} Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. +Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + +Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. + Headings -------- From bd0a257784f37c4badeb94a03e7ce0499118ac58 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sat, 23 Nov 2019 19:45:11 +0100 Subject: [PATCH 66/73] Update CHANGELOG.md --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c79764f..0de2b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,16 @@ Pico Changelog `PicoDeprecated`'s changelog. Please note that BC-breaking changes are only possible with a new major version. +### Version 2.1.0 +Released: - + +``` +* [Changed] Add Pico's official logo and tagline to `content-sample/_meta.md` +* [Changed] Improve `content-sample/theme.md` to show Pico's official logo and + the usage of the new image utility classes of Pico's default theme +* [Changed] Improve Pico docs and PHPDoc class docs +``` + ### Version 2.1.0-beta.1 Released: 2019-11-03 @@ -66,7 +76,7 @@ Released: 2019-11-03 * [Changed] Deprecate `Pico::getBaseThemeUrl()` * [Changed] Replace various `file_exists` calls with proper `is_file` calls * [Changed] Refactor release & build system -* [Changed] Improve Pico docs and PHP class docs +* [Changed] Improve Pico docs and PHPDoc class docs * [Changed] Various small improvements * [Removed] Remove superfluous `base_dir` and `theme_dir` Twig variables * [Removed] Remove `PicoPluginInterface::__construct()` From 35bfdf51b75f84bfde37f9cc619399417dd1106c Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 24 Nov 2019 22:20:48 +0100 Subject: [PATCH 67/73] Sample contents: Add usage of Pico's `pages` variable --- content-sample/index.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/content-sample/index.md b/content-sample/index.md index 182819d..341ebec 100644 --- a/content-sample/index.md +++ b/content-sample/index.md @@ -300,11 +300,21 @@ Check out the following code snippet: The `pages()` function is very powerful and also allows you to return not just -a page's child pages by passing the `depth`, `depthOffset` and `offset` params. -For example, if you pass `pages(depthOffset=-1)`, the list will also include -Pico's main index page (i.e. `content/index.md`). This one is commonly used to -create a theme's main navigation. If you want to learn more, head over to -Pico's complete [`pages()` function documentation][FeaturesPagesFunction]. +a page's child pages by passing the `depth` and `depthOffset` params. For +example, if you pass `pages(depthOffset=-1)`, the list will also include Pico's +main index page (i.e. `content/index.md`). This one is commonly used to create +a theme's main navigation. If you want to learn more, head over to Pico's +complete [`pages()` function documentation][FeaturesPagesFunction]. + +If you want to access the data of a particular page, use Pico's `pages` +variable. Just take `content/_meta.md` in Pico's sample contents for an +example: `content/_meta.md` contains some meta data you might want to use in +your theme. If you want to output the page's `tagline` meta value, use +`{{ pages["_meta"].meta.logo }}`. Don't ever try to use Pico's `pages` variable +as an replacement for Pico's `pages()` function. Its usage looks very similar, +it will kinda work and you might even see it being used in old themes, but be +warned: It slows down Pico. Always use Pico's `pages()` function when iterating +Pico's page list (e.g. `{% for page in pages() %}…{% endfor %}`). #### Twig filters and functions From b25225bbf4010a39729ab252963263ee10f22e20 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 24 Nov 2019 23:40:51 +0100 Subject: [PATCH 68/73] README.md: Update screenshot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62ebe2d..ea1e9de 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Visit us at http://picocms.org/ and see http://picocms.org/about/ for more info. Screenshot ---------- -![Pico Screenshot](https://picocms.github.io/screenshots/pico-20.png) +![Pico Screenshot](https://picocms.github.io/screenshots/pico-21.png) Install ------- From a5ff37e3804e6da46a3b46618d2c6899353b07c9 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 25 Nov 2019 00:41:08 +0100 Subject: [PATCH 69/73] Travis CI: Remove 'sudo' requirement for branch deployment --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c96fb73..71fbcee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,6 @@ jobs: - stage: deploy-branch if: type == "push" && tag IS blank php: 5.6 - sudo: required install: - '[[ ",$DEPLOY_PHPDOC_BRANCHES," == *,"$TRAVIS_BRANCH",* ]] || travis_terminate 0' - install.sh --deploy From 636e8d2a8a14630400dc433ac915ed99fcc691fa Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 25 Nov 2019 00:43:53 +0100 Subject: [PATCH 70/73] Travis CI: Force Composer minimum stability <= beta Unfortunately we must force Composer minimum stability <= beta due to Parsedown 1.8 currently being in beta. Composer AFAIK can't decide this on a per-dependency basis... --- .build/release.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.build/release.sh b/.build/release.sh index 9623b3f..7a2cc1d 100755 --- a/.build/release.sh +++ b/.build/release.sh @@ -50,6 +50,11 @@ composer require --no-update \ "picocms/pico-deprecated $DEPENDENCY_VERSION" echo +# force minimum stability <= beta due to Parsedown 1.8 currently being in beta +if [ "$VERSION_STABILITY" == "stable" ] || [ "$VERSION_STABILITY" == "rc" ]; then + VERSION_STABILITY="beta" +fi + # set minimum stability if [ "$VERSION_STABILITY" != "stable" ]; then echo "Setting minimum stability to '$VERSION_STABILITY'..." From e4e249ae277916dd4d24faa330603cbb69d49a4d Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 24 Nov 2019 22:21:10 +0100 Subject: [PATCH 71/73] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de2b72..12a070d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Pico Changelog are only possible with a new major version. ### Version 2.1.0 -Released: - +Released: 2019-11-24 ``` * [Changed] Add Pico's official logo and tagline to `content-sample/_meta.md` From a87e40fbf0ba764ab89aab7e3feea1523830eab4 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Sun, 24 Nov 2019 23:23:17 +0100 Subject: [PATCH 72/73] Update Pico::VERSION --- lib/Pico.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Pico.php b/lib/Pico.php index 45961ae..7b32854 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -49,7 +49,7 @@ class Pico * * @var string */ - const VERSION = '2.1.0-beta.1'; + const VERSION = '2.1.0'; /** * Pico version ID