Merge branch 'master' of https://github.com/picocms/Pico into feature/bootstrap-theme

Conflicts:
	.gitignore
	composer.json
	themes/default/style.css
This commit is contained in:
theshka 2015-11-24 16:54:42 -06:00
commit ed92a8a730
9 changed files with 177 additions and 56 deletions

23
.gitignore vendored
View file

@ -11,23 +11,22 @@ desktop.ini
._*
# Composer
composer.lock
composer.phar
vendor/*
/composer.lock
/composer.phar
/vendor
# User config
config/config.php
/config/config.php
# User themes
themes/*
!themes/default/index.twig
!themes/default/bootstrap.theme.css
/themes
!/themes/default
# User plugins
plugins/*
!plugins/0?-*
!plugins/1?-*
!plugins/DummyPlugin.php
/plugins
!/plugins/0?-*
!/plugins/1?-*
!/plugins/DummyPlugin.php
# User content
content/*
/content

View file

@ -13,6 +13,7 @@ script:
before_deploy:
- composer install
- composer dump-autoload --optimize
- tar -czf "pico-release-$TRAVIS_TAG.tar.gz" .htaccess README.md CHANGELOG.md CONTRIBUTING.md composer.json composer.lock LICENSE config content-sample lib plugins themes vendor index.php
deploy:

View file

@ -1,6 +1,26 @@
Pico Changelog
==============
### Version 1.0.0
Released: -
```
* [New] This is Picos first stable release! The Pico Community wants to thank
all contributors and users which made this possible!
* [New] New `markdown` filter for Twig to parse markdown strings; Note: If you
want to parse the contents of a page, use the `content` filter instead
* [Changed] Improve documentation
* [Changed] Improve table styling in default theme
* [Changed] Throw a RuntimeException when the `content` dir isn't accessible
* [Changed] Reuse `ParsedownExtra` object; new `onParsedownRegistration` event
* [Fixed] `PicoDeprecated`: Sanitize `content_dir` and `base_url` options when
reading `config.php` in Picos root dir
* [Fixed] Replace `urldecode()` (deprecated RFC 1738) with `rawurldecode()`
(RFC 3986) in `Page::evaluateRequestUrl()`
* [Fixed] #272: Encode URLs using `rawurlencode()` in `Pico::getPageUrl()`
* [Fixed] #274: Prevent double slashes in `base_url`
```
### Version 1.0.0-beta.1
Released: 2015-11-06
@ -53,10 +73,8 @@ Released: 2015-11-06
* [Changed] Pico now implicitly uses a existing `content` directory without
the need to configure this in the `config/config.php` explicitly
* [Changed] Composer: Require a v0.7 release of `erusev/parsedown-extra`
* [Changed] #93, #158: Pico doesn't parse all content files anymore; moved to
`PicoParsePagesContent` plugin, but still impacts performance;
Note: This means `$page['content']` isn't available anymore, but
usually the new `$page['raw_content']` is suitable as replacement.
* [Changed] Moving `license.txt` to `LICENSE`
* [Changed] Moving and reformatting `changelog.txt` to `CHANGELOG.md`
* [Changed] #116: Parse meta headers using the Symfony YAML component
* [Changed] #244: Replace opendir() with scandir()
* [Changed] #246: Move `config.php` to `config/` directory
@ -70,7 +88,11 @@ Released: 2015-11-06
* [Fixed] A vast number of small bugs...
* [Removed] Removing the default Twig cache dir
* [Removed] Removing various empty `index.html` files
* [Removed] Moving Pico's excerpt feature to `PicoExcerpt` plugin
* [Removed] Removing `$pageData['excerpt']`; recoverable with `PicoExcerpt`
* [Removed] #93, #158: Pico doesn't parse all content files anymore; moved to
`PicoParsePagesContent`; i.e. `$pageData['content']` doesn't exist
anymore, use `$pageData['raw_content']` when possible; otherwise
use Twigs new `content` filter (e.g. `{{ "sub/page"|content }}`)
```
### Version 0.9

View file

@ -45,7 +45,7 @@ With this command you can specify a file or folder to limit which files it will
### Keep documentation in sync
Pico accepts the problems of having redundant documentation on different places (concretely Pico's inline user docs, the `README.md` and the website) for the sake of a better user experience. When updating the docs, please make sure the keep them in sync.
Pico accepts the problems of having redundant documentation on different places (concretely Pico's inline user docs, the `README.md` and the website) for the sake of a better user experience. When updating the docs, please make sure to keep them in sync.
If you update the [`README.md`](https://github.com/picocms/Pico/blob/master/README.md) or [`content-sample/index.md`](https://github.com/picocms/Pico/blob/master/content-sample/index.md), please make sure to update the corresponding files in the [`_docs`](https://github.com/picocms/Pico/tree/gh-pages/_docs/) folder of the `gh-pages` branch (i.e. [Pico's website](http://picocms.org/docs.html)) and vice versa. Unfortunately this involves three (!) different markdown parsers. If you're experiencing problems, use Pico's [`erusev/parsedown-extra`](https://github.com/erusev/parsedown-extra) as a reference. You can try to make the contents compatible to [Redcarpet](https://github.com/vmg/redcarpet) by yourself, otherwise please address the issues in your pull request message and we'll take care of it.
@ -63,15 +63,17 @@ For more information please refer to the http://semver.org website.
Branching
---------
The `master` branch contains the current development version of Pico. It is likely *unstable* and *not ready for production use*. However, the `master` branch always consists of a deployable version of Pico.
The `master` branch contains the current development version of Pico. It is likely *unstable* and *not ready for production use*.
However, the `master` branch always consists of a deployable (but not necessarily deployed) version of Pico. As soon as development of a new `MAJOR` or `MINOR` release starts, a separate branch (e.g. `pico-1.1`) is created and a [pull request](https://github.com/picocms/Pico/pulls) is opened to receive the desired feedback.
Pico's actual development happens in separate development branches. Development branches are prefixed by:
- `feature/` for bigger features,
- `enhancement/` for smaller improvements, and
- `bugfix/` for bug fixes.
- `bugfix/` for non-trivial bug fixes.
As soon as development reaches a point where feedback is appreciated, a [pull request](https://github.com/picocms/Pico/pulls) is opened. After some time (very soon for bug fixes, and other improvements should have a reasonable feedback phase) the pull request is merged into `master` and the development branch will be deleted.
As soon as development reaches a point where feedback is appreciated, a pull request is opened. After some time (very soon for bug fixes, and other improvements should have a reasonable feedback phase) the pull request is merged and the development branch will be deleted. Trivial bug fixes which will be part of the next `PATCH` version will be merged directly into `master`.
Build & Release process
-----------------------

View file

@ -46,11 +46,11 @@ Upgrading Pico is very easy: You just have to replace all of Pico's files - that
Pico follows [Semantic Versioning 2.0][SemVer] and uses version numbers like `MAJOR`.`MINOR`.`PATCH`. When we update...
- the `PATCH` version (e.g. `1.0.0` to `1.0.1`), we made backwards-compatible bug fixes. It's then sufficient to extract [Pico's latest release][LatestRelease] to your existing installation directory and overwriting all files.
- the `PATCH` version (e.g. `1.0.0` to `1.0.1`), we made backwards-compatible bug fixes. It's then sufficient to extract [Pico's latest release][LatestRelease] to your existing installation directory and overwriting all files. Alternatively you can either use the [*source code* of Pico's latest release][LatestRelease] or pull from Pico's Git repository, but are then required to update Pico's [composer][] dependencies manually by running `php composer.phar update`.
- the `MINOR` version (e.g. `1.0` to `1.1`), we added functionality in a backwards-compatible manner, but anyway recommend you to "install" Pico newly. Backup all of your files, empty your installation directory and install Pico as elucidated above. You can then copy your `config/config.php` and `content` directory without any change. If applicable, you can also copy the folder of your custom theme within the `themes` directory. Provided that you're using plugins, also copy all of your plugins from the `plugins` directory.
- the `MAJOR` version (e.g. `1.0` to `2.0`), a appropriate upgrade tutorial will be provided.
- the `MAJOR` version (e.g. `1.0` to `2.0`), we made incompatible API changes. We will then provide a appropriate upgrade tutorial.
Upgrading Pico 0.8 or 0.9 to Pico 1.0 is a special case. The new `PicoDeprecated` plugin ensures backwards compatibility, so you basically can follow the above upgrade instructions as if we updated the `MINOR` version. However, we recommend you to take some further steps to confine the necessity of `PicoDeprecated` as far as possible. For more information about what has changed with Pico 1.0 and a step-by-step upgrade tutorial, please refer to the [upgrade page of our website][HelpUpgrade].
@ -115,6 +115,6 @@ You want to contribute to Pico? We really appreciate that! You can help make Pic
[IssuesSearch]: https://github.com/picocms/Pico/search?type=Issues
[PullRequests]: https://github.com/picocms/Pico/pulls
[ContributionGuidelines]: https://github.com/picocms/Pico/blob/master/CONTRIBUTING.md
[EditInlineDocs]: https://github.com/picocms/Pico/blob/master/content-sample/index.md
[EditInlineDocs]: https://github.com/picocms/Pico/edit/master/content-sample/index.md
[EditUserDocs]: https://github.com/picocms/Pico/tree/gh-pages/_docs
[EditDevDocs]: https://github.com/picocms/Pico/tree/gh-pages/_plugin-dev

View file

@ -2,21 +2,31 @@
"name": "picocms/pico",
"type": "library",
"description": "Pico is a flat file CMS, this means there is no administration backend and database to deal with. You simply create .md files in the \"content\" folder and that becomes a page.",
"keywords": ["cms"],
"keywords": ["flat-file","cms","php","twig","markdown"],
"homepage": "http://picocms.org/",
"license": "MIT",
"authors": [
{
"name": "Gilbert Pellegrom",
"email": "gilbert@pellegrom.me"
"email": "gilbert@pellegrom.me",
"role": "Project Founder"
},
{
"name": "The Pico Community",
"homepage": "https://github.com/picocms/Pico/graphs/contributors",
"role": "Contributors"
}
],
"support": {
"docs": "http://picocms.org/docs",
"issues": "https://github.com/picocms/Pico/issues",
"source": "https://github.com/picocms/Pico"
},
"require": {
"php": ">=5.3.6",
"twig/twig": "1.18.*",
"erusev/parsedown-extra": "0.7.*",
"symfony/yaml" : "2.3",
"twbs/bootstrap" : "4.0.0-alpha"
"twig/twig": "^1.18",
"erusev/parsedown-extra": "^0.7",
"symfony/yaml" : "^2.3"
},
"autoload": {
"psr-0": {

View file

@ -62,6 +62,12 @@ Instead of adding your own content to the `content-sample` folder, you should
create your own `content` directory in Pico's root directory. You can then add
and access your contents as described above.
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. So if you want to use a asset (e.g. a image) in one of your content
files, upload it to the (to be created) directory `assets` and use it as
follows: <code>!\[Image Title\](&#37;base_url&#37;/assets/image.png)</code>
### Text File Markup
Text files are marked up using [Markdown][]. They can also contain regular HTML.

View file

@ -137,6 +137,14 @@ class Pico
*/
protected $meta;
/**
* Parsedown Extra instance used for markdown parsing
*
* @see Pico::getParsedown()
* @var ParsedownExtra|null
*/
protected $parsedown;
/**
* Parsed content being served
*
@ -258,7 +266,8 @@ class Pico
* meta headers, processes Markdown, does Twig processing and returns
* the rendered contents.
*
* @return string rendered Pico contents
* @return string rendered Pico contents
* @throws RuntimeException thrown when a not recoverable error occurs
*/
public function run()
{
@ -273,6 +282,11 @@ class Pico
$this->loadConfig();
$this->triggerEvent('onConfigLoaded', array(&$this->config));
// check content dir
if (!is_dir($this->getConfig('content_dir'))) {
throw new RuntimeException('Invalid content directory "' . $this->getConfig('content_dir') . '"');
}
// evaluate request url
$this->evaluateRequestUrl();
$this->triggerEvent('onRequestUrl', array(&$this->requestUrl));
@ -304,6 +318,10 @@ class Pico
$this->meta = $this->parseFileMeta($this->rawContent, $headers);
$this->triggerEvent('onMetaParsed', array(&$this->meta));
// register parsedown
$this->triggerEvent('onParsedownRegistration');
$this->registerParsedown();
// parse file content
$this->triggerEvent('onContentParsing', array(&$this->rawContent));
@ -355,9 +373,15 @@ class Pico
/**
* Loads plugins from Pico::$pluginsDir in alphabetical order
*
* Plugin files may be prefixed by a number (e.g. 00-PicoDeprecated.php)
* to indicate their processing order. You MUST NOT use prefixes between
* 00 and 19 (reserved for built-in plugins).
* Plugin files MAY be prefixed by a number (e.g. 00-PicoDeprecated.php)
* to indicate their processing order. Plugins without a prefix will be
* loaded last. If you want to use a prefix, you MUST consider the
* following directives:
* - 00 to 19: Reserved
* - 20 to 39: Low level code helper plugins
* - 40 to 59: Plugins manipulating routing or the pages array
* - 60 to 79: Plugins hooking into template or markdown parsing
* - 80 to 99: Plugins using the `onPageRendered` event
*
* @see Pico::getPlugin()
* @see Pico::getPlugins()
@ -428,6 +452,10 @@ class Pico
protected function loadConfig()
{
$config = null;
if (file_exists($this->getConfigDir() . 'config.php')) {
require($this->getConfigDir() . 'config.php');
}
$defaultConfig = array(
'site_title' => 'Pico',
'base_url' => '',
@ -442,11 +470,6 @@ class Pico
'timezone' => ''
);
$configFile = $this->getConfigDir() . 'config.php';
if (file_exists($configFile)) {
require $configFile;
}
$this->config = is_array($this->config) ? $this->config : array();
$this->config += is_array($config) ? $config + $defaultConfig : $defaultConfig;
@ -559,7 +582,7 @@ class Pico
if (($pathComponentLength = strpos($pathComponent, '&')) !== false) {
$pathComponent = substr($pathComponent, 0, $pathComponentLength);
}
$this->requestUrl = (strpos($pathComponent, '=') === false) ? urldecode($pathComponent) : '';
$this->requestUrl = (strpos($pathComponent, '=') === false) ? rawurldecode($pathComponent) : '';
}
/**
@ -779,6 +802,28 @@ class Pico
return $this->meta;
}
/**
* Registers the Parsedown Extra markdown parser
*
* @see Pico::getParsedown()
* @return void
*/
protected function registerParsedown()
{
$this->parsedown = new ParsedownExtra();
}
/**
* Returns the Parsedown Extra markdown parser
*
* @see Pico::registerParsedown()
* @return ParsedownExtra|null Parsedown Extra markdown parser
*/
public function getParsedown()
{
return $this->parsedown;
}
/**
* Applies some static preparations to the raw contents of a page,
* e.g. removing the meta header and replacing %base_url%
@ -839,8 +884,11 @@ class Pico
*/
public function parseFileContent($content)
{
$parsedown = new ParsedownExtra();
return $parsedown->text($content);
if ($this->parsedown === null) {
throw new LogicException("Unable to parse file contents: Parsedown instance wasn't registered yet");
}
return $this->parsedown->text($content);
}
/**
@ -1075,22 +1123,42 @@ class Pico
$this->twig = new Twig_Environment($twigLoader, $this->getConfig('twig_config'));
$this->twig->addExtension(new Twig_Extension_Debug());
// register link filter
$this->registerTwigFilter();
}
/**
* Registers Picos additional Twig filters
*
* @return void
*/
protected function registerTwigFilter()
{
$pico = $this;
// link filter
$this->twig->addFilter(new Twig_SimpleFilter('link', array($this, 'getPageUrl')));
// register content filter
$pico = $this;
// content filter
$pages = &$this->pages;
$this->twig->addFilter(new Twig_SimpleFilter('content', function ($pageId) use ($pico, &$pages) {
if (isset($pages[$pageId])) {
$pageData = &$pages[$pageId];
$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 '';
return null;
}));
// markdown filter
$this->twig->addFilter(new Twig_SimpleFilter('markdown', function ($markdown) use ($pico) {
if ($pico->getParsedown() === null) {
throw new LogicException("Unable to parse file contents: Parsedown instance wasn't registered yet");
}
return $pico->getParsedown()->text($markdown);
}));
}
@ -1098,7 +1166,7 @@ class Pico
* Returns the twig template engine
*
* @see Pico::registerTwig()
* @return Twig_Environment|null twig template engine
* @return Twig_Environment|null Twig template engine
*/
public function getTwig()
{
@ -1158,7 +1226,7 @@ class Pico
$this->config['base_url'] =
$protocol . "://" . $_SERVER['HTTP_HOST']
. dirname($_SERVER['SCRIPT_NAME']) . '/';
. rtrim(dirname($_SERVER['SCRIPT_NAME']), '/') . '/';
return $this->getConfig('base_url');
}
@ -1187,7 +1255,13 @@ class Pico
*/
public function getPageUrl($page)
{
return $this->getBaseUrl() . ((!$this->isUrlRewritingEnabled() && !empty($page)) ? '?' : '') . $page;
if (empty($page)) {
return $this->getBaseUrl();
} elseif (!$this->isUrlRewritingEnabled()) {
return $this->getBaseUrl() . '?' . rawurlencode($page);
} else {
return $this->getBaseUrl() . implode('/', array_map('rawurlencode', explode('/', $page)));
}
}
/**
@ -1239,7 +1313,7 @@ class Pico
* @param string $path relative or absolute path
* @return string absolute path
*/
protected function getAbsolutePath($path)
public function getAbsolutePath($path)
{
if (substr($path, 0, 1) !== '/') {
$path = $this->getRootDir() . $path;
@ -1265,8 +1339,7 @@ class Pico
if (!empty($this->plugins)) {
foreach ($this->plugins as $plugin) {
// only trigger events for plugins that implement PicoPluginInterface
// deprecated events (plugins for Pico 0.9 and older) will be
// triggered by the `PicoPluginDeprecated` plugin
// deprecated events (plugins for Pico 0.9 and older) will be triggered by `PicoDeprecated`
if (is_a($plugin, 'PicoPluginInterface')) {
$plugin->handleEvent($eventName, $params);
}

View file

@ -170,11 +170,19 @@ class PicoDeprecated extends AbstractPicoPlugin
protected function loadRootDirConfig(&$realConfig)
{
if (file_exists($this->getRootDir() . 'config.php')) {
// config.php in Pico::$rootDir is deprecated; use Pico::$configDir instead
// config.php in Pico::$rootDir is deprecated
// use config.php in Pico::$configDir instead
$config = null;
require($this->getRootDir() . 'config.php');
if (is_array($config)) {
if (isset($config['base_url'])) {
$config['base_url'] = rtrim($config['base_url'], '/') . '/';
}
if (isset($config['content_dir'])) {
$config['content_dir'] = rtrim($config['content_dir'], '/') . '/';
}
$realConfig = $config + $realConfig;
}
}