- 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
AbstractPicoPlugin::$enabled now defaults to NULL what leaves the decision whether a plugin should be enabled or disabled by default up to Pico (precisely AbstractPicoPlugin::triggerEvent()). If all dependencies of a plugin are fulfilled, Pico enables the plugin by default. Otherwise the plugin is silently disabled (this was the behavior when AbstractPicoPlugin::$enabled was set to TRUE previously).
If a plugin should never be disabled *silently* (e.g. when dealing with security-relevant stuff like access control, or similar), set AbstractPicoPlugin::$enabled to TRUE. If Pico can't fulfill all the plugin's dependencies, it will throw an RuntimeException.
If a plugin rather does some "crazy stuff" a user should really be aware of before using it, you can set AbstractPicoPlugin::$enabled to FALSE. The user will then have to enable the plugin manually. However, if another plugin depends on this plugin, it might get enabled silently nevertheless.
No matter what, the user can always explicitly enable or disable a plugin in Pico's config.
Pico's page tree is a list of all the tree's branches (no matter the depth). Thus, by iterating a array element, you get the nodes of a given branch. All leaf nodes do represent a page, but inner nodes may or may not represent a page (e.g. if there's a `sub/page.md`, but neither a `sub/index.md` nor a `sub.md`, the inner node `sub`, that is the parent of the `sub/page` node, represents no page itself).
A page's file path describes its node's path in the tree (e.g. the page `sub/page.md` is represented by the `sub/page` node, thus a child of the `sub` node and a element of the `sub` branch). However, the index page of a folder (e.g. `sub/index.md`), is *not* a node of the `sub` branch, but rather of the `/` branch. The page's node is not `sub/index`, but `sub`. If two pages are described by the same node (e.g. if both a `sub/index.md` and a `sub.md` exist), the index page takes precedence. Pico's main index page (i.e. `index.md`) is represented by the tree's root node `/` and a special case: it is the only node of the `` (i.e. the empty string) branch.
A node is represented by an array with the keys `id`, `page` and `children`. The `id` key contains a string with the node's name. If the node represents a page, the `page` key is a reference to the page's data array. If the node is a inner node, the `children` key is a reference to its matching branch (i.e. a list of the node's children). The order of a node's children matches the order in Pico's pages array.
If you want to walk the whole page tree, start with the tree's root node at `$pageTree[""]["/"]`. The root node's `children` key is a reference to the `/` branch at `$pageTree["/"]`, that is a list of the root node's direct child nodes and their siblings.
You MUST NOT iterate the page tree itself (i.e. the list of the tree's branches), its order is undefined and the array will be replaced by a non-iterable data structure with Pico 3.0.
Don't lower unregistered meta headers on the first level unsolicited (e.g. `SomeNotRegisteredKey: foobar` in the YAML Frontmatter should result in `['SomeNotRegisteredKey']`, not `['somenotregisteredkey']`). Furthermore, Pico no longer compares registered meta headers in a case-insensitive manner. However, you can now register multiple search strings that are used to find a registered meta header. This is achieved by flipping the meta headers array: Pico 2.0 uses the array key to search for a meta value and the array value to store the found meta value. Previously it was the other way round (what didn't make much sense...).
This is a BC breaking change!
Manipulating Pico's $plugins array is a really bad idea. We've introduced the Pico::loadPlugin() method to safely load plugins at any time, however, Pico might do unexpected things when loading plugins too late. See the class docs of Pico::loadPlugin() for more details. Nevertheless, this change breaks BC to Pico 1.0. However, I don't know a single plugin that relies on manipulating the $plugins array. If you just want to load a plugin manually, use Pico::loadPlugin() instead.
Add new onPagesDiscovered event passing the unsorted pages array, move the $currentPage, $previousPage and $nextPage arguments from the onPagesLoaded event to the new onCurrentPageDiscovered event, remove the $twig argument from the onPageRendering event and rather trigger the new onTwigRegistered event for this. Also add the new onYamlParserRegistered and onParsedownRegistered events passing the YAML parser resp. the Parsedown instance. Allow plugin's to skip a page by setting the $id argument of the onSinglePageLoading event to NULL.
Instead of using `*.config.php` files, use `*.yml` files to configure Pico. YAML is much easier to understand, more user friendly and (at least a bit) more error-tolerant, but still very powerful. Don't break BC by letting `PicoDeprecated` still read `config/config.php`.
As previously announced (see [Upgrade to Pico 1.0 page](http://picocms.org/in-depth/upgrade/)) we'll remove the default plugins `PicoParsePagesContent` and `PicoExcerpt` with the next Pico milestone. Needless to say, that you can still install both plugins without any problem - we'll add them to Pico's official [Plugins collection](http://picocms.org/plugins/) by then. Please note that the disadvantages of these plugins are still critical and we strongly advise to not use them. Please refer to the [Upgrade to Pico 1.0 page](http://picocms.org/in-depth/upgrade/) for details.
Don't use Pico::getAbsolutePath() for $config['content_dir'], just make sure the trailing slash exists. The config.php in Picos root dir should be interpreted exactly like in Pico 0.9 (the option didn't exist in Pico 0.8), thus the path isn't necessarily relative to Picos root dir