summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inc/common.php22
-rw-r--r--inc/parser/metadata.php418
-rw-r--r--inc/parserutils.php92
3 files changed, 532 insertions, 0 deletions
diff --git a/inc/common.php b/inc/common.php
index a073ebeaa..75c3e8574 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -1005,6 +1005,7 @@ function saveWikiText($id,$text,$summary,$minor=false){
}else{
// save file (datadir is created in io_saveFile)
io_saveFile($file,$text);
+ saveMetadata($id, $file, $minor);
$del = false;
}
@@ -1020,6 +1021,27 @@ function saveWikiText($id,$text,$summary,$minor=false){
}
/**
+ * saves the metadata for a page
+ *
+ * @author Esther Brunner <wikidesign@gmail.com>
+ */
+function saveMetadata($id, $file, $minor){
+ global $INFO;
+
+ $user = $_SERVER['REMOTE_USER'];
+
+ $meta = array();
+ if (!$INFO['exists']){ // newly created
+ $meta['date']['created'] = @filectime($file);
+ if ($user) $meta['creator'] = $INFO['userinfo']['name'];
+ } elseif (!$minor) { // non-minor modification
+ $meta['date']['modified'] = @filemtime($file);
+ if ($user) $meta['contributor'][$user] = $INFO['userinfo']['name'];
+ }
+ p_set_metadata($id, $meta, true);
+}
+
+/**
* moves the current version to the attic and returns its
* revision date
*
diff --git a/inc/parser/metadata.php b/inc/parser/metadata.php
new file mode 100644
index 000000000..0ba59c65e
--- /dev/null
+++ b/inc/parser/metadata.php
@@ -0,0 +1,418 @@
+<?php
+/**
+ * Renderer for metadata
+ *
+ * @author Esther Brunner <wikidesign@gmail.com>
+ */
+
+if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
+
+if ( !defined('DOKU_LF') ) {
+ // Some whitespace to help View > Source
+ define ('DOKU_LF',"\n");
+}
+
+if ( !defined('DOKU_TAB') ) {
+ // Some whitespace to help View > Source
+ define ('DOKU_TAB',"\t");
+}
+
+require_once DOKU_INC . 'inc/parser/renderer.php';
+
+/**
+ * The Renderer
+ */
+class Doku_Renderer_metadata extends Doku_Renderer {
+
+ var $doc = '';
+ var $meta = array();
+
+ var $headers = array();
+ var $capture = true;
+ var $store = '';
+
+ function meta($data) {
+ if (is_array($data)){
+ $this->meta = $data;
+ if (!$this->meta['title']) $this->meta['title'] = $data['first_heading'];
+ }
+ }
+
+ function document_start(){
+ //reset some variables
+ $this->meta['description']['abstract'] = '';
+ $this->meta['description']['tableofcontents'] = array();
+ $this->meta['relation']['haspart'] = array();
+ $this->meta['relation']['references'] = array();
+ $this->headers = array();
+ }
+
+ function document_end(){
+ if (!$this->meta['description']['abstract']){
+ // cut off too long abstracts
+ $this->doc = trim($this->doc);
+ if (strlen($this->doc) > 500)
+ $this->doc = substr($this->doc, 0, 500).'…';
+ $this->meta['description']['abstract'] = $this->doc;
+ }
+ }
+
+ function header($text, $level, $pos) {
+ global $conf;
+
+ // create a unique header id
+ $hid = $this->_headerToLink($text,'true');
+
+ //handle TOC
+ if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){
+ // the TOC is one of our standard ul list arrays ;-)
+ $this->meta['description']['tableofcontents'][] = array(
+ 'hid' => $hid,
+ 'title' => $text,
+ 'type' => 'ul',
+ 'level' => $level-$conf['toptoclevel']+1
+ );
+ }
+
+ // add to summary
+ if ($this->capture && ($level > 1)) $this->doc .= DOKU_LF.$text.DOKU_LF;
+ }
+
+ function section_open($level){}
+ function section_close(){}
+
+ function cdata($text){
+ if ($this->capture) $this->doc .= $text;
+ }
+
+ function p_open(){
+ if ($this->capture) $this->doc .= DOKU_LF;
+ }
+
+ function p_close(){
+ if ($this->capture){
+ if (strlen($this->doc) > 250) $this->capture = false;
+ else $this->doc .= DOKU_LF;
+ }
+ }
+
+ function linebreak(){
+ if ($this->capture) $this->doc .= DOKU_LF;
+ }
+
+ function hr(){
+ if ($this->capture){
+ if (strlen($this->doc) > 250) $this->capture = false;
+ else $this->doc .= DOKU_LF.'----------'.DOKU_LF;
+ }
+ }
+
+ function strong_open(){}
+ function strong_close(){}
+
+ function emphasis_open(){}
+ function emphasis_close(){}
+
+ function underline_open(){}
+ function underline_close(){}
+
+ function monospace_open(){}
+ function monospace_close(){}
+
+ function subscript_open(){}
+ function subscript_close(){}
+
+ function superscript_open(){}
+ function superscript_close(){}
+
+ function deleted_open(){}
+ function deleted_close(){}
+
+ /**
+ * Callback for footnote start syntax
+ *
+ * All following content will go to the footnote instead of
+ * the document. To achieve this the previous rendered content
+ * is moved to $store and $doc is cleared
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ function footnote_open() {
+ if ($this->capture){
+ // move current content to store and record footnote
+ $this->store = $this->doc;
+ $this->doc = '';
+ }
+ }
+
+ /**
+ * Callback for footnote end syntax
+ *
+ * All rendered content is moved to the $footnotes array and the old
+ * content is restored from $store again
+ *
+ * @author Andreas Gohr
+ */
+ function footnote_close() {
+ if ($this->capture){
+ // restore old content
+ $this->doc = $this->store;
+ $this->store = '';
+ }
+ }
+
+ function listu_open(){
+ if ($this->capture) $this->doc .= DOKU_LF;
+ }
+
+ function listu_close(){
+ if ($this->capture && (strlen($this->doc) > 250)) $this->capture = false;
+ }
+
+ function listo_open(){
+ if ($this->capture) $this->doc .= DOKU_LF;
+ }
+
+ function listo_close(){
+ if ($this->capture && (strlen($this->doc) > 250)) $this->capture = false;
+ }
+
+ function listitem_open($level){
+ if ($this->capture) $this->doc .= str_repeat(DOKU_TAB, $level).'* ';
+ }
+
+ function listitem_close(){
+ if ($this->capture) $this->doc .= DOKU_LF;
+ }
+
+ function listcontent_open(){}
+ function listcontent_close(){}
+
+ function unformatted($text){
+ if ($this->capture) $this->doc .= $text;
+ }
+
+ function php($text){}
+
+ function html($text){}
+
+ function preformatted($text){
+ if ($this->capture) $this->doc .= $text;
+ }
+
+ function file($text){
+ if ($this->capture){
+ $this->doc .= DOKU_LF.$text;
+ if (strlen($this->doc) > 250) $this->capture = false;
+ else $this->doc .= DOKU_LF;
+ }
+ }
+
+ function quote_open(){
+ if ($this->capture) $this->doc .= DOKU_LF.DOKU_TAB.'“';
+ }
+
+ function quote_close(){
+ if ($this->capture){
+ $this->doc .= '”';
+ if (strlen($this->doc) > 250) $this->capture = false;
+ else $this->doc .= DOKU_LF;
+ }
+ }
+
+ function code($text, $language = NULL){
+ if ($this->capture){
+ $this->doc .= DOKU_LF.$text;
+ if (strlen($this->doc) > 250) $this->capture = false;
+ else $this->doc .= DOKU_LF;
+ }
+ }
+
+ function acronym($acronym){
+ if ($this->capture) $this->doc .= $acronym;
+ }
+
+ function smiley($smiley){
+ if ($this->capture) $this->doc .= $smiley;
+ }
+
+ function entity($entity){
+ if ($this->capture) $this->doc .= $entity;
+ }
+
+ function multiplyentity($x, $y){
+ if ($this->capture) $this->doc .= $x.'×'.$y;
+ }
+
+ function singlequoteopening(){
+ if ($this->capture) $this->doc .= '‘';
+ }
+
+ function singlequoteclosing(){
+ if ($this->capture) $this->doc .= '’';
+ }
+
+ function doublequoteopening(){
+ if ($this->capture) $this->doc .= '“';
+ }
+
+ function doublequoteclosing(){
+ if ($this->capture) $this->doc .= '”';
+ }
+
+ function camelcaselink($link) {
+ $this->internallink($link, $link);
+ }
+
+ function locallink($hash, $name = NULL){}
+
+ /**
+ * keep track of internal links in $this->meta['relation']['references']
+ */
+ function internallink($id, $name = NULL){
+ global $ID;
+
+ $default = $this->_simpleTitle($id);
+
+ // first resolve and clean up the $id
+ resolve_pageid(getNS($ID), $id, $exists);
+ list($id, $hash) = split('#', $id, 2);
+
+ // set metadata
+ $this->meta['relation']['references'][$id] = $exists;
+ // $data = array('relation' => array('isreferencedby' => array($ID => true)));
+ // p_set_metadata($id, $data);
+
+ // add link title to summary
+ if ($this->capture){
+ $name = $this->_getLinkTitle($name, $default, $id);
+ $this->doc .= $name;
+ }
+ }
+
+ function externallink($url, $name = NULL){
+ if ($this->capture){
+ if ($name) $this->doc .= $name;
+ else $this->doc .= '<'.$url.'>';
+ }
+ }
+
+ function interwikilink($match, $name = NULL, $wikiName, $wikiUri){
+ if ($this->capture){
+ list($wikiUri, $hash) = explode('#', $wikiUri, 2);
+ $name = $this->_getLinkTitle($name, $wikiName.'>'.$wikiUri);
+ $this->doc .= $name;
+ }
+ }
+
+ function windowssharelink($url, $name = NULL){
+ if ($this->capture){
+ if ($name) $this->doc .= $name;
+ else $this->doc .= '<'.$url.'>';
+ }
+ }
+
+ function emaillink($address, $name = NULL){
+ if ($this->capture){
+ if ($name) $this->doc .= $name;
+ else $this->doc .= '<'.$address.'>';
+ }
+ }
+
+ function internalmedia($src, $title=NULL, $align=NULL, $width=NULL,
+ $height=NULL, $cache=NULL, $linking=NULL){
+ if ($this->capture && $title) $this->doc .= '['.$title.']';
+ }
+
+ function externalmedia($src, $title=NULL, $align=NULL, $width=NULL,
+ $height=NULL, $cache=NULL, $linking=NULL){
+ if ($this->capture && $title) $this->doc .= '['.$title.']';
+ }
+
+ function rss($url){}
+
+ function table_open($maxcols = NULL, $numrows = NULL){}
+ function table_close(){}
+
+ function tablerow_open(){}
+ function tablerow_close(){}
+
+ function tableheader_open($colspan = 1, $align = NULL){}
+ function tableheader_close(){}
+
+ function tablecell_open($colspan = 1, $align = NULL){}
+ function tablecell_close(){}
+
+ //----------------------------------------------------------
+ // Utils
+
+ /**
+ * Removes any Namespace from the given name but keeps
+ * casing and special chars
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ function _simpleTitle($name){
+ global $conf;
+
+ if($conf['useslash']){
+ $nssep = '[:;/]';
+ }else{
+ $nssep = '[:;]';
+ }
+ $name = preg_replace('!.*'.$nssep.'!','',$name);
+ //if there is a hash we use the ancor name only
+ $name = preg_replace('!.*#!','',$name);
+ return $name;
+ }
+
+ /**
+ * Creates a linkid from a headline
+ *
+ * @param string $title The headline title
+ * @param boolean $create Create a new unique ID?
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ function _headerToLink($title, $create=false) {
+ $title = str_replace(':','',cleanID($title,true)); //force ASCII
+ $title = ltrim($title,'0123456789._-');
+ if(empty($title)) $title='section';
+
+ if($create){
+ // make sure tiles are unique
+ $num = '';
+ while(in_array($title.$num,$this->headers)){
+ ($num) ? $num++ : $num = 1;
+ }
+ $title = $title.$num;
+ $this->headers[] = $title;
+ }
+
+ return $title;
+ }
+
+ /**
+ * Construct a title and handle images in titles
+ *
+ * @author Harry Fuecks <hfuecks@gmail.com>
+ */
+ function _getLinkTitle($title, $default, $id=NULL) {
+ global $conf;
+
+ $isImage = FALSE;
+ if (is_null($title)){
+ if ($conf['useheading'] && $id){
+ $heading = p_get_first_heading($id);
+ if ($heading) return $heading;
+ }
+ return $default;
+ } else if (is_string($title)){
+ return $title;
+ } else if (is_array($title)){
+ return '['.$title.']';
+ }
+ }
+
+}
+
+//Setup VIM: ex: et ts=4 enc=utf-8 : \ No newline at end of file
diff --git a/inc/parserutils.php b/inc/parserutils.php
index b295ae152..981f33cc1 100644
--- a/inc/parserutils.php
+++ b/inc/parserutils.php
@@ -233,6 +233,98 @@ function p_get_instructions($text){
}
/**
+ * returns the metadata of a page
+ *
+ * @author Esther Brunner <esther@kaffeehaus.ch>
+ */
+function p_get_metadata($id, $key=false, $render=false){
+ $file = metaFN($id, '.meta');
+
+ if (@file_exists($file)) $meta = unserialize(io_readFile($file, false));
+ else $meta = array();
+
+ // metadata has never been rendered before - do it!
+ if ($render && !$meta['description']['abstract']){
+ $meta = p_render_metadata($id, $meta);
+ io_saveFile($file, serialize($meta));
+ }
+
+ // filter by $key
+ if ($key){
+ list($key, $subkey) = explode(' ', $key, 2);
+ if (trim($subkey)) return $meta[$key][$subkey];
+ else return $meta[$key];
+ }
+
+ return $meta;
+}
+
+/**
+ * sets metadata elements of a page
+ *
+ * @author Esther Brunner <esther@kaffeehaus.ch>
+ */
+function p_set_metadata($id, $data, $render=false){
+ if (!is_array($data)) return false;
+
+ $orig = p_get_metadata($id);
+
+ // render metadata first?
+ if ($render) $meta = p_render_metadata($id, $orig);
+ else $meta = $orig;
+
+ // now add the passed metadata
+ $protected = array('description', 'date', 'contributor');
+ foreach ($data as $key => $value){
+
+ // be careful with sub-arrays of $meta['relation']
+ if ($key == 'relation'){
+ foreach ($value as $subkey => $subvalue){
+ $meta[$key][$subkey] = array_merge($meta[$key][$subkey], $subvalue);
+ }
+
+ // be careful with some senisitive arrays of $meta
+ } elseif (in_array($key, $protected)){
+ if (is_array($value)){
+ $meta[$key] = array_merge($meta[$key], $value);
+ }
+
+ // no special treatment for the rest
+ } else {
+ $meta[$key] = $value;
+ }
+ }
+
+ // save only if metadata changed
+ if ($meta == $orig) return true;
+ return io_saveFile(metaFN($id, '.meta'), serialize($meta));
+}
+
+/**
+ * renders the metadata of a page
+ *
+ * @author Esther Brunner <esther@kaffeehaus.ch>
+ */
+function p_render_metadata($id, $orig){
+ require_once DOKU_INC."inc/parser/metadata.php";
+
+ // get instructions
+ $instructions = p_cached_instructions(wikiFN($id));
+
+ // set up the renderer
+ $renderer = & new Doku_Renderer_metadata();
+ $renderer->meta = $orig;
+
+ // loop through the instructions
+ foreach ($instructions as $instruction){
+ // execute the callback against the renderer
+ call_user_func_array(array(&$renderer, $instruction[0]), $instruction[1]);
+ }
+
+ return $renderer->meta;
+}
+
+/**
* returns all available parser syntax modes in correct order
*
* @author Andreas Gohr <andi@splitbrain.org>