Selaa lähdekoodia

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

kpavlov 12 vuotta sitten
vanhempi
commit
f553e211c5
8 muutettua tiedostoa jossa 426 lisäystä ja 344 poistoa
  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/*
 
 # User config
-config.php
+config.php

+ 3 - 1
README.md

@@ -1,4 +1,6 @@
 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
  * [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
  * [Changed] Replaced glob_recursive with get_files
 

+ 14 - 6
content/index.md

@@ -6,16 +6,16 @@ keywords: Pico CMS
 
 ## 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
 
 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`)
-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>
 	<thead>
@@ -56,7 +56,7 @@ There are also certain variables that you can use in your text files:
 
 ### 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
 in config.php to your theme folder.
 
@@ -99,7 +99,15 @@ Pages can be used like:
 	{% endfor %}
 &lt;/ul&gt;</pre>
 
+### Plugins
+
+See [http://pico.dev7studios.com/plugins](http://pico.dev7studios.com/plugins)
+
 ### 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
-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('CACHE_DIR', LIB_DIR .'cache/');
 
+date_default_timezone_set('UTC');
+
 require(ROOT_DIR .'vendor/autoload.php');
 require(LIB_DIR .'pico.php');
 $pico = new Pico();

+ 334 - 335
lib/pico.php

@@ -7,349 +7,348 @@ use \Michelf\MarkdownExtra;
  * @author Gilbert Pellegrom
  * @link http://pico.dev7studios.com
  * @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
         $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);
-        $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)
-    {
-        $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,
             '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)
-    {
-        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_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))
-            );
-            $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)
 	{
 		
@@ -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)
 	{
 		
@@ -76,4 +86,4 @@ class Pico_Plugin {
 	
 }
 
-// End of file
+// End of file

+ 53 - 0
vendor/composer/installed.json

@@ -102,5 +102,58 @@
         "keywords": [
             "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"
+        ]
     }
 ]