├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── PicoFilePrefixes.php ├── README.md └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Linux 2 | *~ 3 | *.swp 4 | 5 | # Windows 6 | Thumbs.db 7 | desktop.ini 8 | 9 | # Mac OS X 10 | .DS_Store 11 | ._* 12 | 13 | # Composer 14 | /composer.lock 15 | /composer.phar 16 | /vendor 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Pico File Prefixes Changelog 2 | ============================ 3 | 4 | ### Version 2.0.0 5 | Released: 2018-07-24 6 | 7 | ``` 8 | * [New] Updated to Pico 2.0+ 9 | * [Changed] Improved docs 10 | ``` 11 | 12 | ### Version 1.0.3 13 | Released: 2017-06-24 14 | 15 | ``` 16 | * [Fixed] Fix PHP syntax error in `README.md` 17 | * [Changed] Add `type: pico-plugin` to `composer.json` 18 | ``` 19 | 20 | ### Version 1.0.2 21 | Released: 2016-09-17 22 | 23 | ``` 24 | * [New] Add `CHANGELOG.md` 25 | * [Changed] Rename composer package to `phrozenbyte/pico-file-prefixes` and 26 | publish it on Packagist 27 | ``` 28 | 29 | ### Version 1.0.1 30 | Released: 2016-07-11 31 | 32 | ``` 33 | * [Fixed] Fix file path regex generation 34 | * [Changed] Enhance `README.md` 35 | ``` 36 | 37 | ### Version 1.0.0 38 | Released: 2016-07-08 39 | 40 | Initial release 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Daniel Rudolf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PicoFilePrefixes.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * SPDX-License-Identifier: MIT 10 | * License-Filename: LICENSE 11 | */ 12 | 13 | /** 14 | * Pico file prefixes plugin - drop file prefixes from page URLs 15 | * 16 | * PicoFilePrefixes removes file prefixes (e.g. date identifiers) from page 17 | * URLs (e.g. http://example.com/20160707.page --> http://example.com/page). 18 | * This makes organizing your files on the filesystem easier than ever before, 19 | * while maintaining user-friendly URLs. 20 | * 21 | * @author Daniel Rudolf 22 | * @link http://picocms.org 23 | * @license http://opensource.org/licenses/MIT The MIT License 24 | * @version 2.0.0 25 | */ 26 | class PicoFilePrefixes extends AbstractPicoPlugin 27 | { 28 | /** 29 | * API version used by this plugin 30 | * 31 | * @var int 32 | */ 33 | const API_VERSION = 2; 34 | 35 | /** 36 | * Regex pattern matching directory paths with prefixed files 37 | * 38 | * @var string 39 | */ 40 | protected $filePathRegex = ''; 41 | 42 | /** 43 | * List of pages whose URL has been altered 44 | * 45 | * @var array 46 | */ 47 | protected $prefixPages = array(); 48 | 49 | /** 50 | * Prepares the plugin's configuration and the file path regex 51 | * 52 | * @see DummyPlugin::onConfigLoaded() 53 | */ 54 | public function onConfigLoaded(array &$config) 55 | { 56 | $defaultPluginConfig = array( 57 | 'recursiveDirs' => array('blog'), 58 | 'dirs' => array() 59 | ); 60 | 61 | if (!isset($config['PicoFilePrefixes']) || !is_array($config['PicoFilePrefixes'])) { 62 | $config['PicoFilePrefixes'] = $defaultPluginConfig; 63 | $this->filePathRegex = '#^(blog(?:/.+)?)$#'; 64 | return; 65 | } 66 | 67 | $config['PicoFilePrefixes'] += $defaultPluginConfig; 68 | 69 | if (empty($config['PicoFilePrefixes']['recursiveDirs']) && empty($config['PicoFilePrefixes']['dirs'])) { 70 | // disable plugin when no dirs were configured 71 | $this->setEnabled(false); 72 | return; 73 | } 74 | 75 | // prepare file path regex 76 | $this->filePathRegex = '#^'; 77 | if (!empty($config['PicoFilePrefixes']['recursiveDirs'])) { 78 | if (in_array('.', $config['PicoFilePrefixes']['recursiveDirs'])) { 79 | // enable plugin for any directory 80 | $this->filePathRegex = '#^.+$#'; 81 | return; 82 | } 83 | 84 | $this->filePathRegex .= '(?:' . implode('|', array_map(function ($recursiveDir) { 85 | return preg_quote($recursiveDir, '#'); 86 | }, $config['PicoFilePrefixes']['recursiveDirs'])) . ')(?:/.+)?'; 87 | 88 | if (!empty($config['PicoFilePrefixes']['dirs'])) { 89 | $this->filePathRegex .= '|'; 90 | } 91 | } 92 | if (!empty($config['PicoFilePrefixes']['dirs'])) { 93 | $this->filePathRegex .= implode('|', array_map(function ($dir) { 94 | return preg_quote($dir, '#'); 95 | }, $config['PicoFilePrefixes']['dirs'])); 96 | } 97 | $this->filePathRegex .= '$#'; 98 | } 99 | 100 | /** 101 | * Rewrites shortened URLs to their matching file on the filesystem 102 | * 103 | * @see DummyPlugin::onRequestFile() 104 | */ 105 | public function onRequestFile(&$file) 106 | { 107 | if (file_exists($file)) { 108 | // don't do anything when the requested file exists 109 | return; 110 | } 111 | 112 | $contentDir = $this->getConfig('content_dir'); 113 | $contentDirLength = strlen($contentDir); 114 | if (substr($file, 0, $contentDirLength) === $contentDir) { 115 | $filePath = dirname(substr($file, $contentDirLength)); 116 | if (preg_match($this->filePathRegex, $filePath)) { 117 | $filePattern = (($filePath !== '.') ? $filePath . '/' : '') . '*.' . basename($file); 118 | $matchingFiles = glob($contentDir . $filePattern); 119 | if ($matchingFiles) { 120 | // use the last matching file in alphabetic order 121 | // (i.e. the "highest" prefix wins) 122 | $file = end($matchingFiles); 123 | } 124 | } 125 | } 126 | } 127 | 128 | /** 129 | * Alters URLs of prefixed files 130 | * 131 | * @see DummyPlugin::onPagesLoaded() 132 | */ 133 | public function onPagesLoaded(array &$pages) 134 | { 135 | foreach ($pages as &$pageData) { 136 | $filePath = dirname($pageData['id']); 137 | if (preg_match($this->filePathRegex, $filePath)) { 138 | $file = basename($pageData['id']); 139 | if (($removeIdentifierPos = strpos($file, '.')) === false) { 140 | // don't alter URLs of files without a prefix 141 | continue; 142 | } 143 | 144 | $file = (($filePath !== '.') ? $filePath . '/' : '') . substr($file, $removeIdentifierPos + 1); 145 | if (isset($pages[$file])) { 146 | // don't do anything when a file of this name exists 147 | continue; 148 | } 149 | 150 | if (isset($this->prefixPages[$file])) { 151 | // found a conflicting file 152 | // alter the URL of the file with the "highest" prefix only 153 | if (strcmp($pageData['id'], $this->prefixPages[$file]['id']) <= 0) { 154 | continue; 155 | } 156 | 157 | // restore the URL of the previously altered page 158 | $pages[$this->prefixPages[$file]['id']]['url'] = $this->prefixPages[$file]['url']; 159 | } 160 | 161 | $this->prefixPages[$file] = $pageData; 162 | $pageData['url'] = $this->getPageUrl($file); 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pico File Prefixes 2 | ================== 3 | 4 | This is the repository of Pico's official file prefixes plugin. 5 | 6 | Pico is a stupidly simple, blazing fast, flat file CMS. See http://picocms.org/ for more info. 7 | 8 | `PicoFilePrefixes` removes file prefixes (e.g. date identifiers) from page URLs. For example, the blog article `content/blog/20160707.visit-us-on-github.md` normally corresponds to the page URL http://example.com/pico/blog/20160707.visit-us-on-github, however, by installing this plugin, the article will be accessible through the much more user-friendly URL http://example.com/pico/blog/visit-us-on-github. This makes organizing your website's pages on the filesystem easier than ever before. 9 | 10 | Install 11 | ------- 12 | 13 | You can either install `PicoFilePrefixes` using [Composer](https://getcomposer.org/), or using a single PHP plugin file. We recommend you to use Composer whenever possible, because it allows you to keep the plugin up-to-date way more easily. 14 | 15 | If you use a Composer-based installation of Pico and want to either remove or install `PicoFilePrefixes`, simply open a shell on your server and navigate to Pico's install directory (e.g. `/var/www/html/pico/`). Run `composer remove phrozenbyte/pico-file-prefixes` to remove `PicoFilePrefixes`, or run `composer require phrozenbyte/pico-file-prefixes` (via [Packagist.org](https://packagist.org/packages/phrozenbyte/pico-file-prefixes)) to install `PicoFilePrefixes`. 16 | 17 | If you really want to install `PicoFilePrefixes` using a single PHP plugin file, [download the latest release](https://github.com/PhrozenByte/pico-file-prefixes/releases/latest) and upload the `PicoFilePrefixes.php` file to the `plugins` directory of your Pico installation (e.g. `/var/www/html/pico/plugins/`). 18 | 19 | `PicoFilePrefixes` requires Pico 2.0+ 20 | 21 | Config 22 | ------ 23 | 24 | The plugin recursively drops file prefixes of all files in the `content/blog/` directory by default. You can specify other directories by altering the `PicoFilePrefixes.recursiveDirs` and/or `PicoFilePrefixes.dirs` config variables (both expect YAML lists) in your `config/config.php`. The former parses all files of a directory recursively (i.e. including all its subfolders), whereas the latter parses just files in this particular directory. The default configuration looks like the following: 25 | 26 | ```yaml 27 | PicoFilePrefixes: 28 | recursiveDirs: 29 | - blog 30 | dirs: [] 31 | ``` 32 | 33 | If you want to additionally enable the plugin for the `content/showcase/` directory, try the following configuration: 34 | 35 | ```yaml 36 | PicoFilePrefixes: 37 | recursiveDirs: 38 | - blog 39 | - showcase 40 | dirs: [] 41 | ``` 42 | 43 | If you want to enable the plugin for any folder, try the following: 44 | 45 | ```yaml 46 | PicoFilePrefixes: 47 | recursiveDirs: 48 | - . 49 | dirs: [] 50 | ``` 51 | 52 | To enable the plugin for pages in the `content/misc/` directory only (i.e. not including subfolders like `content/misc/sub/`), try the following: 53 | 54 | ```yaml 55 | PicoFilePrefixes: 56 | recursiveDirs: [] 57 | dirs: 58 | - misc 59 | ``` 60 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phrozenbyte/pico-file-prefixes", 3 | "type": "pico-plugin", 4 | "description": "This is Pico's official file prefixes plugin, to drop file prefixes from page URLs. Pico is a stupidly simple, blazing fast, flat file CMS.", 5 | "keywords": [ "pico", "picocms", "picocms-plugin", "pico-file-prefixes", "seo", "url-shortener" ], 6 | "homepage": "http://picocms.org/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Daniel Rudolf", 11 | "homepage": "http://daniel-rudolf.de/", 12 | "role": "Lead Developer" 13 | }, 14 | { 15 | "name": "Contributors", 16 | "homepage": "https://github.com/PhrozenByte/pico-file-prefixes/graphs/contributors" 17 | } 18 | ], 19 | "support": { 20 | "docs": "http://picocms.org/docs/pico-file-prefixes/", 21 | "issues": "https://github.com/PhrozenByte/pico-file-prefixes/issues", 22 | "source": "https://github.com/PhrozenByte/pico-file-prefixes" 23 | }, 24 | "require": { 25 | "php": ">=5.3.0" 26 | }, 27 | "autoload": { 28 | "classmap": [ "PicoFilePrefixes.php" ] 29 | } 30 | } 31 | --------------------------------------------------------------------------------