Browse Source

Merge branch 'master' of github.com:gilbitron/Pico

kpavlov 12 years ago
parent
commit
f553e211c5
8 changed files with 426 additions and 344 deletions
  1. 1 1
      .gitignore
  2. 3 1
      README.md
  3. 8 0
      changelog.txt
  4. 14 6
      content/index.md
  5. 2 0
      index.php
  6. 334 335
      lib/pico.php
  7. 11 1
      plugins/pico_plugin.php
  8. 53 0
      vendor/composer/installed.json

+ 1 - 1
.gitignore

@@ -20,4 +20,4 @@ themes/*
 !themes/default/*
 !themes/default/*
 
 
 # User config
 # User config
-config.php
+config.php

+ 3 - 1
README.md

@@ -1,4 +1,6 @@
 Pico
 Pico
 ====
 ====
 
 
-Pico is a stupidly simple, blazing fast, flat file CMS. See http://pico.dev7studios.com for more info.
+Pico is a stupidly simple, blazing fast, flat file CMS. See http://pico.dev7studios.com for more info.
+
+Want to say thanks? [Consider tipping me](https://www.gittip.com/gilbitron).

+ 8 - 0
changelog.txt

@@ -9,6 +9,14 @@
  * [changed] get_files no longer gets dotfiles
  * [changed] get_files no longer gets dotfiles
  * [fixed] Issues with updating Pico install once in use
  * [fixed] Issues with updating Pico install once in use
 
 
+2013.09.04 - version 0.7
+ * [New] Added before_read_file_meta and get_page_data plugin hooks to customize page meta data
+ * [Changed] Make get_files() ignore dotfiles
+ * [Changed] Make get_pages() ignore Emacs and temp files
+ * [Changed] Use composer version of Markdown
+ * [Changed] Other small tweaks
+ * [Fixed] Date warnings and other small bugs
+
 2013.05.07 - version 0.6.2
 2013.05.07 - version 0.6.2
  * [Changed] Replaced glob_recursive with get_files
  * [Changed] Replaced glob_recursive with get_files
 
 

+ 14 - 6
content/index.md

@@ -6,16 +6,16 @@ keywords: Pico CMS
 
 
 ## Welcome to Pico
 ## Welcome to Pico
 
 
-Congratulations you have successfully installed [Pico](http://pico.dev7studios.com). Pico is a stupidly simple, blazing fast, flat file CMS.
+Congratulations, you have successfully installed [Pico](http://pico.dev7studios.com). Pico is a stupidly simple, blazing fast, flat file CMS.
 
 
 ### Creating Content
 ### Creating Content
 
 
 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"
 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. For example this file is called `index.md` and is shown as the main landing page. 
+folder and that becomes a page. For example, this file is called `index.md` and is shown as the main landing page. 
 
 
-If you created folder within the content folder (e.g. `content/sub`) and put an `index.md` inside it, you can access that folder at the URL 
+If you create a folder within the content folder (e.g. `content/sub`) and put an `index.md` inside it, you can access that folder at the URL 
 `http://yousite.com/sub`. If you want another page within the sub folder, simply create a text file with the corresponding name (e.g. `content/sub/page.md`)
 `http://yousite.com/sub`. If you want another page within the sub folder, simply create a text file with the corresponding name (e.g. `content/sub/page.md`)
-and will be able to access it from the URL `http://yousite.com/sub/page`. Below we've shown some examples of content locations and their corresponing URL's:
+and you will be able to access it from the URL `http://yousite.com/sub/page`. Below we've shown some examples of content locations and their corresponing URL's:
 
 
 <table>
 <table>
 	<thead>
 	<thead>
@@ -56,7 +56,7 @@ There are also certain variables that you can use in your text files:
 
 
 ### Themes
 ### Themes
 
 
-You can create themes for your Pico installation and in the "themes" folder. Check out the default theme for an example of a theme. Pico uses
+You can create themes for your Pico installation in the "themes" folder. Check out the default theme for an example of a theme. Pico uses
 [Twig](http://twig.sensiolabs.org/documentation) for it's templating engine. You can select your theme by setting the `$config['theme']` variable
 [Twig](http://twig.sensiolabs.org/documentation) for it's templating engine. You can select your theme by setting the `$config['theme']` variable
 in config.php to your theme folder.
 in config.php to your theme folder.
 
 
@@ -99,7 +99,15 @@ Pages can be used like:
 	{% endfor %}
 	{% endfor %}
 &lt;/ul&gt;</pre>
 &lt;/ul&gt;</pre>
 
 
+### Plugins
+
+See [http://pico.dev7studios.com/plugins](http://pico.dev7studios.com/plugins)
+
 ### Config
 ### Config
 
 
 You can override the default Pico settings (and add your own custom settings) by editing config.php in the root Pico directory. The config.php file
 You can override the default Pico settings (and add your own custom settings) by editing config.php in the root Pico directory. The config.php file
-list all of the settings and their defaults. To override a setting simply uncomment it in config.php and set your custom value.
+lists all of the settings and their defaults. To override a setting, simply uncomment it in config.php and set your custom value.
+
+### Documentation
+
+For more help have a look at the Pico documentation at [http://pico.dev7studios.com/docs](http://pico.dev7studios.com/docs)

+ 2 - 0
index.php

@@ -8,6 +8,8 @@ define('PLUGINS_DIR', ROOT_DIR .'plugins/');
 define('THEMES_DIR', ROOT_DIR .'themes/');
 define('THEMES_DIR', ROOT_DIR .'themes/');
 define('CACHE_DIR', LIB_DIR .'cache/');
 define('CACHE_DIR', LIB_DIR .'cache/');
 
 
+date_default_timezone_set('UTC');
+
 require(ROOT_DIR .'vendor/autoload.php');
 require(ROOT_DIR .'vendor/autoload.php');
 require(LIB_DIR .'pico.php');
 require(LIB_DIR .'pico.php');
 $pico = new Pico();
 $pico = new Pico();

+ 334 - 335
lib/pico.php

@@ -7,349 +7,348 @@ use \Michelf\MarkdownExtra;
  * @author Gilbert Pellegrom
  * @author Gilbert Pellegrom
  * @link http://pico.dev7studios.com
  * @link http://pico.dev7studios.com
  * @license http://opensource.org/licenses/MIT
  * @license http://opensource.org/licenses/MIT
- * @version 0.6.2
+ * @version 0.7
  */
  */
-class Pico
-{
-
-    private $plugins;
-
-    /**
-     * The constructor carries out all the processing in Pico.
-     * Does URL routing, Markdown processing and Twig processing.
-     */
-    public function __construct()
-    {
-        // Load plugins
-        $this->load_plugins();
-        $this->run_hooks('plugins_loaded');
-
-        // Get request url and script url
-        $url = '';
-        $request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
-        $script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
-
-        // Get our url path and trim the / of the left and the right
-        if ($request_url != $script_url) $url = trim(preg_replace('/' . str_replace('/', '\/', str_replace('index.php', '', $script_url)) . '/', '', $request_url, 1), '/');
-        $url = preg_replace('/\?.*/', '', $url); // Strip query string
-        $this->run_hooks('request_url', array(&$url));
-
-        // Get the file path
-        if ($url) $file = CONTENT_DIR . $url;
-        else $file = CONTENT_DIR . 'index';
-
-        // Load the file
-        if (is_dir($file)) $file = CONTENT_DIR . $url . '/index' . CONTENT_EXT;
-        else $file .= CONTENT_EXT;
-
-        $this->run_hooks('before_load_content', array(&$file));
-        if (file_exists($file)) {
-            $content = file_get_contents($file);
-        } else {
-            $this->run_hooks('before_404_load_content', array(&$file));
-            $content = file_get_contents(CONTENT_DIR . '404' . CONTENT_EXT);
-            header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
-            $this->run_hooks('after_404_load_content', array(&$file, &$content));
-        }
-        $this->run_hooks('after_load_content', array(&$file, &$content));
-
-        // Load the settings
-        $settings = $this->get_config();
-        $this->run_hooks('config_loaded', array(&$settings));
-
-        $meta = $this->read_file_meta($content);
-        $this->run_hooks('file_meta', array(&$meta));
-        $content = $this->parse_content($content);
-        $this->run_hooks('content_parsed', array(&$content));
-
-        // Get all the pages
-        $pages = $this->get_pages($settings['base_url'], $settings['pages_order_by'], $settings['pages_order'],
-            $settings['meta_max_length'], $settings['excerpt_length']);
-        $prev_page = array();
-        $current_page = array();
-        $next_page = array();
-        while ($current_page = current($pages)) {
-            if ((isset($meta['title'])) && ($meta['title'] == $current_page['title'])) {
-                break;
-            }
-            next($pages);
-        }
-        $prev_page = next($pages);
-        prev($pages);
-        $next_page = prev($pages);
-        $this->run_hooks('get_pages', array(&$pages, &$current_page, &$prev_page, &$next_page));
-
-        // Load the theme
-        $this->run_hooks('before_twig_register');
-        Twig_Autoloader::register();
-        $loader = new Twig_Loader_Filesystem(THEMES_DIR . $settings['theme']);
-        $twig = new Twig_Environment($loader, $settings['twig_config']);
-        $twig->addExtension(new Twig_Extension_Debug());
-        $twig_vars = array(
-            'config' => $settings,
-            'base_dir' => rtrim(ROOT_DIR, '/'),
-            'base_url' => $settings['base_url'],
-            'theme_dir' => THEMES_DIR . $settings['theme'],
-            'theme_url' => $settings['base_url'] . '/' . basename(THEMES_DIR) . '/' . $settings['theme'],
-            'site_title' => $settings['site_title'],
-            'meta' => $meta,
-            'content' => $content,
-            'pages' => $pages,
-            'prev_page' => $prev_page,
-            'current_page' => $current_page,
-            'next_page' => $next_page,
-            'is_front_page' => $url ? false : true,
-        );
+class Pico {
+
+	private $plugins;
+
+	/**
+	 * The constructor carries out all the processing in Pico.
+	 * Does URL routing, Markdown processing and Twig processing.
+	 */
+	public function __construct()
+	{
+		// Load plugins
+		$this->load_plugins();
+		$this->run_hooks('plugins_loaded');
+		
+		// Get request url and script url
+		$url = '';
+		$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
+		$script_url  = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
+
+		// Get our url path and trim the / of the left and the right
+		if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/');
+		$url = preg_replace('/\?.*/', '', $url); // Strip query string
+		$this->run_hooks('request_url', array(&$url));
+
+		// Get the file path
+		if($url) $file = CONTENT_DIR . $url;
+		else $file = CONTENT_DIR .'index';
+
+		// Load the file
+		if(is_dir($file)) $file = CONTENT_DIR . $url .'/index'. CONTENT_EXT;
+		else $file .= CONTENT_EXT;
+
+		$this->run_hooks('before_load_content', array(&$file));
+		if(file_exists($file)){
+			$content = file_get_contents($file);
+		} else {
+			$this->run_hooks('before_404_load_content', array(&$file));
+			$content = file_get_contents(CONTENT_DIR .'404'. CONTENT_EXT);
+			header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
+			$this->run_hooks('after_404_load_content', array(&$file, &$content));
+		}
+		$this->run_hooks('after_load_content', array(&$file, &$content));
+		
+		// Load the settings
+		$settings = $this->get_config();
+		$this->run_hooks('config_loaded', array(&$settings));
+
+		$meta = $this->read_file_meta($content);
+		$this->run_hooks('file_meta', array(&$meta));
+		$content = $this->parse_content($content);
+		$this->run_hooks('content_parsed', array(&$content));
+		
+		// Get all the pages
+		$pages = $this->get_pages($settings['base_url'], $settings['pages_order_by'], $settings['pages_order'], $settings['excerpt_length']);
+		$prev_page = array();
+		$current_page = array();
+		$next_page = array();
+		while($current_page = current($pages)){
+			if((isset($meta['title'])) && ($meta['title'] == $current_page['title'])){
+				break;
+			}
+			next($pages);
+		}
+		$prev_page = next($pages);
+		prev($pages);
+		$next_page = prev($pages);
+		$this->run_hooks('get_pages', array(&$pages, &$current_page, &$prev_page, &$next_page));
+
+		// Load the theme
+		$this->run_hooks('before_twig_register');
+		Twig_Autoloader::register();
+		$loader = new Twig_Loader_Filesystem(THEMES_DIR . $settings['theme']);
+		$twig = new Twig_Environment($loader, $settings['twig_config']);
+		$twig->addExtension(new Twig_Extension_Debug());
+		$twig_vars = array(
+			'config' => $settings,
+			'base_dir' => rtrim(ROOT_DIR, '/'),
+			'base_url' => $settings['base_url'],
+			'theme_dir' => THEMES_DIR . $settings['theme'],
+			'theme_url' => $settings['base_url'] .'/'. basename(THEMES_DIR) .'/'. $settings['theme'],
+			'site_title' => $settings['site_title'],
+			'meta' => $meta,
+			'content' => $content,
+			'pages' => $pages,
+			'prev_page' => $prev_page,
+			'current_page' => $current_page,
+			'next_page' => $next_page,
+			'is_front_page' => $url ? false : true,
+		);
         // use a custom template if specified Template: [filename] in page meta e.g. Template: spesh to try and use spesh.html in theme folder
         // use a custom template if specified Template: [filename] in page meta e.g. Template: spesh to try and use spesh.html in theme folder
         $template = ((isset($meta['template']) && file_exists($twig_vars['theme_dir'] . '/' . $meta['template'] . $settings['template_ext'])) ? $meta['template'] . $settings['template_ext'] : 'index' . $settings['template_ext']);
         $template = ((isset($meta['template']) && file_exists($twig_vars['theme_dir'] . '/' . $meta['template'] . $settings['template_ext'])) ? $meta['template'] . $settings['template_ext'] : 'index' . $settings['template_ext']);
 
 
-        $this->run_hooks('before_render', array(&$twig_vars, &$twig));
+		$this->run_hooks('before_render', array(&$twig_vars, &$twig));
         $output = $twig->render($template, $twig_vars);
         $output = $twig->render($template, $twig_vars);
-        $this->run_hooks('after_render', array(&$output));
-        echo $output;
-    }
-
-    /**
-     * Load any plugins
-     */
-    private function load_plugins()
-    {
-        $this->plugins = array();
-        $plugins = $this->get_files(PLUGINS_DIR, '.php');
-        if (!empty($plugins)) {
-            foreach ($plugins as $plugin) {
-                include_once($plugin);
-                $plugin_name = preg_replace("/\\.[^.\\s]{3}$/", '', basename($plugin));
-                if (class_exists($plugin_name)) {
-                    $obj = new $plugin_name;
-                    $this->plugins[] = $obj;
-                }
-            }
-        }
-    }
-
-    /**
-     * Parses the content using Markdown
-     *
-     * @param string $content the raw txt content
-     * @return string $content the Markdown formatted content
-     */
+		$this->run_hooks('after_render', array(&$output));
+		echo $output;
+	}
+	
+	/**
+	 * Load any plugins
+	 */
+	private function load_plugins()
+	{
+		$this->plugins = array();
+		$plugins = $this->get_files(PLUGINS_DIR, '.php');
+		if(!empty($plugins)){
+			foreach($plugins as $plugin){
+				include_once($plugin);
+				$plugin_name = preg_replace("/\\.[^.\\s]{3}$/", '', basename($plugin));
+				if(class_exists($plugin_name)){
+					$obj = new $plugin_name;
+					$this->plugins[] = $obj;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Parses the content using Markdown
+	 *
+	 * @param string $content the raw txt content
+	 * @return string $content the Markdown formatted content
+	 */
     private function parse_content(&$content)
     private function parse_content(&$content)
-    {
-        $content = preg_replace('#/\*.+?\*/#s', '', $content); // Remove comments and meta
-        $content = str_replace('%base_url%', $this->base_url(), $content);
-        $content = MarkdownExtra::defaultTransform($content);
-
-        return $content;
-    }
-
-    /**
-     * Parses the file meta from the txt file header. Meta keys are converted to lowercase automatically.
-     *
-     * @param string $content the raw txt content
-     * @return array $headers an array of meta values
-     */
-    private function read_file_meta($content)
-    {
-        global $config;
-
-        $headers = array();
-        if (preg_match_all("/\/\*(.+?)\*\/(.*)/ms", $content, $h_and_c)) {
-            preg_match_all('/(\w+)\s*:\s*(.*)/i', $h_and_c[1][0], $m);
-            for ($i = 0; $i < count($m[0]); $i++) {
-                $headers[strtolower($m[1][$i])] = trim($m[2][$i]);
-            }
-        }
-
-        if (isset($headers['date'])) $headers['date_formatted'] = date($config['date_format'], strtotime($headers['date']));
-
-        if (empty($headers['title'])) {
-            preg_match('/^(.+?)[ ]*\n(=+|-+)[ ]*\n+/imu', $content, $matches);
-            if (count($matches) > 0) {
-                $headers['title'] = $matches[1];
-            } else {
-                preg_match('/^\#{1}([^\#].*)$/imu', $content, $matches);
-                if (count($matches) > 0) {
-                    $headers['title'] = $matches[1];
-                }
-            }
-        }
-
-        return $headers;
-    }
-
-    /**
-     * Loads the config
-     *
-     * @return array $config an array of config values
-     */
-    private function get_config()
-    {
-        global $config;
-        @include_once(ROOT_DIR . 'config.php');
-
-        $defaults = array(
-            'site_title' => 'Pico',
-            'base_url' => $this->base_url(),
-            'theme' => 'default',
-            'date_format' => 'jS M Y',
-            'twig_config' => array('cache' => false, 'autoescape' => false, 'debug' => false),
-            'pages_order_by' => 'alpha',
-            'pages_order' => 'asc',
+	{
+		$content = preg_replace('#/\*.+?\*/#s', '', $content); // Remove comments and meta
+		$content = str_replace('%base_url%', $this->base_url(), $content);
+		$content = MarkdownExtra::defaultTransform($content);
+
+		return $content;
+	}
+
+	/**
+     * Parses the file meta from the txt file header.
+     * Meta keys are converted to lowercase automatically.
+	 *
+	 * @param string $content the raw txt content
+	 * @return array $headers an array of meta values
+	 */
+	private function read_file_meta($content)
+	{
+		global $config;
+		
+		$headers = array(
+			'title'       	=> 'Title',
+			'description' 	=> 'Description',
+			'author' 		=> 'Author',
+			'date' 			=> 'Date',
+			'robots'     	=> 'Robots'
+		);
+
+		// Add support for custom headers by hooking into the headers array
+		$this->run_hooks('before_read_file_meta', array(&$headers));
+
+	 	foreach ($headers as $field => $regex){
+			if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi', $content, $match) && $match[1]){
+				$headers[ $field ] = trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $match[1]));
+			} else {
+				$headers[ $field ] = '';
+			}
+		}
+		
+		if(isset($headers['date'])) $headers['date_formatted'] = date($config['date_format'], strtotime($headers['date']));
+
+		return $headers;
+	}
+
+	/**
+	 * Loads the config
+	 *
+	 * @return array $config an array of config values
+	 */
+	private function get_config()
+	{
+		global $config;
+		@include_once(ROOT_DIR .'config.php');
+
+		$defaults = array(
+			'site_title' => 'Pico',
+			'base_url' => $this->base_url(),
+			'theme' => 'default',
+			'date_format' => 'jS M Y',
+			'twig_config' => array('cache' => false, 'autoescape' => false, 'debug' => false),
+			'pages_order_by' => 'alpha',
+			'pages_order' => 'asc',
             'excerpt_length' => 50,
             'excerpt_length' => 50,
             'template_ext' => '.html'
             'template_ext' => '.html'
-        );
-
-        if (is_array($config)) $config = array_merge($defaults, $config);
-        else $config = $defaults;
-
-        return $config;
-    }
-
-    /**
-     * Get a list of pages
-     *
-     * @param string $base_url the base URL of the site
-     * @param string $order_by order by "alpha" or "date"
-     * @param string $order order "asc" or "desc"
-     * @return array $sorted_pages an array of pages
-     */
+		);
+
+		if(is_array($config)) $config = array_merge($defaults, $config);
+		else $config = $defaults;
+
+		return $config;
+	}
+	
+	/**
+	 * Get a list of pages
+	 *
+	 * @param string $base_url the base URL of the site
+	 * @param string $order_by order by "alpha" or "date"
+	 * @param string $order order "asc" or "desc"
+	 * @return array $sorted_pages an array of pages
+	 */
     private function get_pages($base_url, $order_by = 'alpha', $order = 'asc', $meta_max_length = 2048, $excerpt_length = 50)
     private function get_pages($base_url, $order_by = 'alpha', $order = 'asc', $meta_max_length = 2048, $excerpt_length = 50)
-    {
-        global $config;
-
-        $pages = $this->get_files(CONTENT_DIR, CONTENT_EXT);
-        $sorted_pages = array();
-        $date_id = 0;
-        foreach ($pages as $key => $page) {
-            // Skip 404
-            if (basename($page) == '404' . CONTENT_EXT) {
-                unset($pages[$key]);
-                continue;
-            }
-
-            // Ignore Emacs (and Nano) temp files
-            if (in_array(substr($page, -1), array('~', '#'))) {
-                unset($pages[$key]);
-                continue;
-            }
-            // Get title and format $page
+	{
+		global $config;
+		
+		$pages = $this->get_files(CONTENT_DIR, CONTENT_EXT);
+		$sorted_pages = array();
+		$date_id = 0;
+		foreach($pages as $key=>$page){
+			// Skip 404
+			if(basename($page) == '404'. CONTENT_EXT){
+				unset($pages[$key]);
+				continue;
+			}
+
+			// Ignore Emacs (and Nano) temp files
+			if (in_array(substr($page, -1), array('~','#'))) {
+				unset($pages[$key]);
+				continue;
+			}			
+			// Get title and format $page
             $page_content = file_get_contents($page, NULL, NULL, 0, $meta_max_length);
             $page_content = file_get_contents($page, NULL, NULL, 0, $meta_max_length);
-            $page_meta = $this->read_file_meta($page_content);
-            if (!$page_meta) trigger_error("$page meta not read");
-            $page_content = $this->parse_content($page_content);
-            $url = str_replace(CONTENT_DIR, $base_url . '/', $page);
-            $url = str_replace('index' . CONTENT_EXT, '', $url);
-            $url = str_replace(CONTENT_EXT, '', $url);
-            $data = array();
-            // these are generic fields that are added
-            foreach ($page_meta as $meta_key => $value) {
-                $data[$meta_key] = isset($page_meta[$meta_key]) ? $value : null;
-            }
-            // these are special fields and need to be overwritten
-            $extras = array(
-                'url' => $url,
-                'date_formatted' => isset($page_meta['date']) ? date($config['date_format'], strtotime($page_meta['date'])) : null,
-                'content' => $page_content,
-                'excerpt' => isset($page_meta['excerpt']) ? $page_meta['excerpt'] : $this->limit_words(strip_tags($page_content), $excerpt_length),
+			$page_meta = $this->read_file_meta($page_content);
+			$page_content = $this->parse_content($page_content);
+			$url = str_replace(CONTENT_DIR, $base_url .'/', $page);
+			$url = str_replace('index'. CONTENT_EXT, '', $url);
+			$url = str_replace(CONTENT_EXT, '', $url);
+			$data = array(
+				'title' => isset($page_meta['title']) ? $page_meta['title'] : '',
+				'url' => $url,
+				'author' => isset($page_meta['author']) ? $page_meta['author'] : '',
+				'date' => isset($page_meta['date']) ? $page_meta['date'] : '',
+				'date_formatted' => isset($page_meta['date']) ? date($config['date_format'], strtotime($page_meta['date'])) : '',
+				'content' => $page_content,
+				'excerpt' => $this->limit_words(strip_tags($page_content), $excerpt_length),
                 'last_modified' => new DateTime('@' . filemtime($page))
                 'last_modified' => new DateTime('@' . filemtime($page))
-            );
-            $data = array_merge($data, $extras);
-            if ($order_by == 'date') {
-                $sorted_pages[$page_meta['date'] . $date_id] = $data;
-                $date_id++;
-            } else $sorted_pages[] = $data;
-        }
-
-        if ($order == 'desc') krsort($sorted_pages);
-        else ksort($sorted_pages);
-
-        return $sorted_pages;
-    }
-
-    /**
-     * Processes any hooks and runs them
-     *
-     * @param string $hook_id the ID of the hook
-     * @param array $args optional arguments
-     */
-    private function run_hooks($hook_id, $args = array())
-    {
-        if (!empty($this->plugins)) {
-            foreach ($this->plugins as $plugin) {
-                if (is_callable(array($plugin, $hook_id))) {
-                    call_user_func_array(array($plugin, $hook_id), $args);
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper function to work out the base URL
-     *
-     * @return string the base url
-     */
-    private function base_url()
-    {
-        global $config;
-        if (isset($config['base_url']) && $config['base_url']) return $config['base_url'];
-
-        $url = '';
-        $request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
-        $script_url = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
-        if ($request_url != $script_url) $url = trim(preg_replace('/' . str_replace('/', '\/', str_replace('index.php', '', $script_url)) . '/', '', $request_url, 1), '/');
-
-        $protocol = $this->get_protocol();
-        return rtrim(str_replace($url, '', $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), '/');
-    }
-
-    /**
-     * Tries to guess the server protocol. Used in base_url()
-     *
-     * @return string the current protocol
-     */
-    private function get_protocol()
-    {
-        preg_match("|^HTTP[S]?|is", $_SERVER['SERVER_PROTOCOL'], $m);
-        return strtolower($m[0]);
-    }
-
-    /**
-     * Helper function to recusively get all files in a directory
-     *
-     * @param string $directory start directory
-     * @param string $ext optional limit to file extensions
-     * @return array the matched files
-     */
-    private function get_files($directory, $ext = '')
-    {
-        $array_items = array();
-        if ($handle = opendir($directory)) {
-            while (false !== ($file = readdir($handle))) {
-                if ($file != "." && $file != "..") {
-                    if (is_dir($directory . "/" . $file)) {
-                        $array_items = array_merge($array_items, $this->get_files($directory . "/" . $file, $ext));
-                    } else {
-                        $file = $directory . "/" . $file;
-                        if (!$ext || strstr($file, $ext)) $array_items[] = preg_replace("/\/\//si", "/", $file);
-                    }
-                }
-            }
-            closedir($handle);
-        }
-        return $array_items;
-    }
-
-    /**
-     * Helper function to limit the words in a string
-     *
-     * @param string $string the given string
-     * @param int $word_limit the number of words to limit to
-     * @return string the limited string
-     */
-    private function limit_words($string, $word_limit)
-    {
-        $words = explode(' ', $string);
-        return trim(implode(' ', array_splice($words, 0, $word_limit))) . '...';
-    }
-}
+			);
+
+			// Extend the data provided with each page by hooking into the data array
+			$this->run_hooks('get_page_data', array(&$data, $page_meta));
+
+			if($order_by == 'date' && isset($page_meta['date'])){
+				$sorted_pages[$page_meta['date'].$date_id] = $data;
+				$date_id++;
+			}
+			else $sorted_pages[] = $data;
+		}
+		
+		if($order == 'desc') krsort($sorted_pages);
+		else ksort($sorted_pages);
+		
+		return $sorted_pages;
+	}
+	
+	/**
+	 * Processes any hooks and runs them
+	 *
+	 * @param string $hook_id the ID of the hook
+	 * @param array $args optional arguments
+	 */
+	private function run_hooks($hook_id, $args = array())
+	{
+		if(!empty($this->plugins)){
+			foreach($this->plugins as $plugin){
+				if(is_callable(array($plugin, $hook_id))){
+					call_user_func_array(array($plugin, $hook_id), $args);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Helper function to work out the base URL
+	 *
+	 * @return string the base url
+	 */
+	private function base_url()
+	{
+		global $config;
+		if(isset($config['base_url']) && $config['base_url']) return $config['base_url'];
+
+		$url = '';
+		$request_url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
+		$script_url  = (isset($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : '';
+		if($request_url != $script_url) $url = trim(preg_replace('/'. str_replace('/', '\/', str_replace('index.php', '', $script_url)) .'/', '', $request_url, 1), '/');
+
+		$protocol = $this->get_protocol();
+		return rtrim(str_replace($url, '', $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']), '/');
+	}
+
+	/**
+	 * Tries to guess the server protocol. Used in base_url()
+	 *
+	 * @return string the current protocol
+	 */
+	private function get_protocol()
+	{
+		preg_match("|^HTTP[S]?|is",$_SERVER['SERVER_PROTOCOL'],$m);
+		return strtolower($m[0]);
+	}
+	     
+	/**
+	 * Helper function to recusively get all files in a directory
+	 *
+	 * @param string $directory start directory
+	 * @param string $ext optional limit to file extensions
+	 * @return array the matched files
+	 */ 
+	private function get_files($directory, $ext = '')
+	{
+	    $array_items = array();
+	    if($handle = opendir($directory)){
+	        while(false !== ($file = readdir($handle))){
+	            if(preg_match("/^(^\.)/", $file) === 0){
+	                if(is_dir($directory. "/" . $file)){
+	                    $array_items = array_merge($array_items, $this->get_files($directory. "/" . $file, $ext));
+	                } else {
+	                    $file = $directory . "/" . $file;
+	                    if(!$ext || strstr($file, $ext)) $array_items[] = preg_replace("/\/\//si", "/", $file);
+	                }
+	            }
+	        }
+	        closedir($handle);
+	    }
+	    return $array_items;
+	}
+	
+	/**
+	 * Helper function to limit the words in a string
+	 *
+	 * @param string $string the given string
+	 * @param int $word_limit the number of words to limit to
+	 * @return string the limited string
+	 */ 
+	private function limit_words($string, $word_limit)
+	{
+		$words = explode(' ',$string);
+		return trim(implode(' ', array_splice($words, 0, $word_limit))) .'...';
+	}
+
+}

+ 11 - 1
plugins/pico_plugin.php

@@ -44,6 +44,11 @@ class Pico_Plugin {
 		
 		
 	}
 	}
 	
 	
+	public function before_read_file_meta(&$headers)
+	{
+		
+	}
+	
 	public function file_meta(&$meta)
 	public function file_meta(&$meta)
 	{
 	{
 		
 		
@@ -54,6 +59,11 @@ class Pico_Plugin {
 		
 		
 	}
 	}
 	
 	
+	public function get_page_data(&$data, $page_meta)
+	{
+		
+	}
+	
 	public function get_pages(&$pages, &$current_page, &$prev_page, &$next_page)
 	public function get_pages(&$pages, &$current_page, &$prev_page, &$next_page)
 	{
 	{
 		
 		
@@ -76,4 +86,4 @@ class Pico_Plugin {
 	
 	
 }
 }
 
 
-// End of file
+// End of file

+ 53 - 0
vendor/composer/installed.json

@@ -102,5 +102,58 @@
         "keywords": [
         "keywords": [
             "templating"
             "templating"
         ]
         ]
+    },
+    {
+        "name": "michelf/php-markdown",
+        "version": "1.3",
+        "version_normalized": "1.3.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/michelf/php-markdown.git",
+            "reference": "fcdd3e0781ae40c2b9847874e0755ff4f5559688"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/michelf/php-markdown/zipball/fcdd3e0781ae40c2b9847874e0755ff4f5559688",
+            "reference": "fcdd3e0781ae40c2b9847874e0755ff4f5559688",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2013-04-11 18:53:11",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-lib": "1.3.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Michelf": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "BSD-3-Clause"
+        ],
+        "authors": [
+            {
+                "name": "Michel Fortin",
+                "email": "michel.fortin@michelf.ca",
+                "homepage": "http://michelf.ca/",
+                "role": "Developer"
+            },
+            {
+                "name": "John Gruber",
+                "homepage": "http://daringfireball.net/"
+            }
+        ],
+        "description": "PHP Markdown",
+        "homepage": "http://michelf.ca/projects/php-markdown/",
+        "keywords": [
+            "markdown"
+        ]
     }
     }
 ]
 ]