TNH Patches for DokuWiki

This wiki runs on DokuWiki “Hrun” with some additional customization.

(The following patch was made against an earlier release.)

dokuwiki-20120125-tnh.diff
diff --git a/.gitignore b/.gitignore
index f9155ea..f72c863 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,5 @@
 !/lib/plugins/admin.php
 !/lib/plugins/index.html
 !/lib/plugins/syntax.php
+/inc/preload.php
+/conf/*.html
diff --git a/_test/cases/inc/indexer_idx_indexlengths.test.php b/_test/cases/inc/indexer_idx_indexlengths.test.php
index a0f7c92..9b5ca5c 100644
--- a/_test/cases/inc/indexer_idx_indexlengths.test.php
+++ b/_test/cases/inc/indexer_idx_indexlengths.test.php
@@ -13,7 +13,7 @@ class indexer_idx_indexlengths_test extends UnitTestCase {
         // one word should return the index
         $ref[] = 8;
         sort($ref);
-        $result = idx_indexLengths(&$filter);
+        $result = idx_indexLengths($filter);
         sort($result);
         $this->assertIdentical($result, $ref);
     }
@@ -27,7 +27,7 @@ class indexer_idx_indexlengths_test extends UnitTestCase {
         // more words should return the indexes
         $ref = array(4, 7, 8);
         sort($ref);
-        $result = idx_indexLengths(&$filter);
+        $result = idx_indexLengths($filter);
         sort($result);
         $this->assertIdentical($result, $ref);
     }
@@ -50,7 +50,7 @@ class indexer_idx_indexlengths_test extends UnitTestCase {
         }
         closedir($dir);
         sort($ref);
-        $result = idx_indexLengths(&$filter);
+        $result = idx_indexLengths($filter);
         sort($result);
         $this->assertIdentical($result, $ref);
     }
diff --git a/_test/tests.ini b/_test/tests.ini
index cb16d4f..276ee6e 100644
--- a/_test/tests.ini
+++ b/_test/tests.ini
@@ -1,4 +1,4 @@
-TEST_ENABLED = 0
+TEST_ENABLED = 1
 
 ; For performing "web tests" - PHP scripts acting as web browser
 WEB_TEST_URL = http://localhost/dokuwiki
@@ -9,4 +9,4 @@ REMOTE_TEST_URL = http://localhost/dokuwiki/test/index.php
 ;PROXY = http://proxyuser:proxypwd@proxy.yourdomain.com:8080
 
 ; Path to Simple Test
-SIMPLE_TEST = ../../simpletest/
+SIMPLE_TEST = /home/tnharris/Documents/simpletest/
diff --git a/inc/FeedParser.php b/inc/FeedParser.php
index e5f1fb6..8f5b3b1 100644
--- a/inc/FeedParser.php
+++ b/inc/FeedParser.php
@@ -17,7 +17,8 @@ class FeedParser extends SimplePie {
      */
     function FeedParser(){
         $this->SimplePie();
-        $this->enable_cache(false);
+        $this->enable_cache(true);
+        $this->set_cache_class('FeedParser_Cache');
         $this->set_file_class('FeedParser_File');
     }
 
@@ -74,3 +75,41 @@ class FeedParser_File extends SimplePie_File {
     }
 
 }
+
+/**
+ * Our own cache handler
+ */
+class FeedParser_Cache extends SimplePie_Cache {
+    var $cache;    // cache file name
+
+    /**
+     * Initialize the cache using DokuWiki conf setting.
+     * Ignores $location
+     */
+    function FeedParser_Cache($location, $filename, $extension) {
+        global $conf;
+        $this->cache = $conf['cachedir'].'/'.$filename[0].'/'.$filename.'.'.$extension;
+        io_makeFileDir($this->cache);
+    }
+
+    function save($data) {
+        return io_saveFile($this->cache, serialize($data));
+    }
+
+    function load() {
+        return unserialize(io_readFile($this->cache, false));
+    }
+
+    function mtime() {
+        return @filemtime($this->cache);
+    }
+
+    function touch() {
+        return @touch($this->cache);
+    }
+
+    function unlink() {
+        return @unlink($this->cache);
+    }
+
+}
diff --git a/inc/HTTPClient.php b/inc/HTTPClient.php
index 6419503..6f9a2d0 100644
--- a/inc/HTTPClient.php
+++ b/inc/HTTPClient.php
@@ -279,6 +279,10 @@ class HTTPClient {
             $headers['Proxy-Authorization'] = 'Basic '.base64_encode($this->proxy_user.':'.$this->proxy_pass);
         }
 
+        // ignore aborts and timeouts
+        $php_timeout = ini_get('max_execution_time');
+        set_time_limit(0);
+
         // stop time
         $start = time();
 
@@ -297,6 +301,7 @@ class HTTPClient {
             if (!$socket){
                 $this->status = -100;
                 $this->error = "Could not connect to $server:$port\n$errstr ($errno)";
+                set_time_limit($php_timeout);
                 return false;
             }
 
@@ -349,6 +354,7 @@ class HTTPClient {
                 $this->status = -100;
                 $this->error = 'Failed writing to socket';
                 unset($this->connections[$connectionId]);
+                set_time_limit($php_timeout);
                 return false;
             }
             $written += $ret;
@@ -364,11 +370,13 @@ class HTTPClient {
                 $this->status = -100;
                 $this->error = sprintf('Timeout while reading headers (%.3fs)',$this->_time() - $this->start);
                 unset($this->connections[$connectionId]);
+                set_time_limit($php_timeout);
                 return false;
             }
             if(feof($socket)){
                 $this->error = 'Premature End of File (socket)';
                 unset($this->connections[$connectionId]);
+                set_time_limit($php_timeout);
                 return false;
             }
             usleep(1000);
@@ -381,9 +389,11 @@ class HTTPClient {
         if($this->max_bodysize && preg_match('/\r?\nContent-Length:\s*(\d+)\r?\n/i',$r_headers,$match)){
             if($match[1] > $this->max_bodysize){
                 $this->error = 'Reported content length exceeds allowed response size';
-                if ($this->max_bodysize_abort)
+                if ($this->max_bodysize_abort) {
                     unset($this->connections[$connectionId]);
+                    set_time_limit($php_timeout);
                     return false;
+                }
             }
         }
 
@@ -391,6 +401,7 @@ class HTTPClient {
         if (!preg_match('/^HTTP\/(\d\.\d)\s*(\d+).*?\n/', $r_headers, $m)) {
             $this->error = 'Server returned bad answer';
             unset($this->connections[$connectionId]);
+            set_time_limit($php_timeout);
             return false;
         }
         $this->status = $m[2];
@@ -423,9 +434,11 @@ class HTTPClient {
 
             if (empty($this->resp_headers['location'])){
                 $this->error = 'Redirect but no Location Header found';
+                set_time_limit($php_timeout);
                 return false;
             }elseif($this->redirect_count == $this->max_redirect){
                 $this->error = 'Maximum number of redirects exceeded';
+                set_time_limit($php_timeout);
                 return false;
             }else{
                 $this->redirect_count++;
@@ -440,6 +453,7 @@ class HTTPClient {
                                                           $this->resp_headers['location'];
                     }
                 }
+                set_time_limit($php_timeout);
                 // perform redirected request, always via GET (required by RFC)
                 return $this->sendRequest($this->resp_headers['location'],array(),'GET');
             }
@@ -449,6 +463,7 @@ class HTTPClient {
         if($this->header_regexp && !preg_match($this->header_regexp,$r_headers)){
             $this->error = 'The received headers did not match the given regexp';
             unset($this->connections[$connectionId]);
+            set_time_limit($php_timeout);
             return false;
         }
 
@@ -461,12 +476,14 @@ class HTTPClient {
                     if(feof($socket)){
                         $this->error = 'Premature End of File (socket)';
                         unset($this->connections[$connectionId]);
+                        set_time_limit($php_timeout);
                         return false;
                     }
                     if(time()-$start > $this->timeout){
                         $this->status = -100;
                         $this->error = sprintf('Timeout while reading chunk (%.3fs)',$this->_time() - $this->start);
                         unset($this->connections[$connectionId]);
+                        set_time_limit($php_timeout);
                         return false;
                     }
                     $byte = fread($socket,1);
@@ -485,6 +502,7 @@ class HTTPClient {
                     $this->error = 'Allowed response size exceeded';
                     if ($this->max_bodysize_abort){
                         unset($this->connections[$connectionId]);
+                        set_time_limit($php_timeout);
                         return false;
                     } else {
                         break;
@@ -498,6 +516,7 @@ class HTTPClient {
                     $this->status = -100;
                     $this->error = sprintf('Timeout while reading response (%.3fs)',$this->_time() - $this->start);
                     unset($this->connections[$connectionId]);
+                    set_time_limit($php_timeout);
                     return false;
                 }
                 $r_body .= fread($socket,4096);
@@ -506,6 +525,7 @@ class HTTPClient {
                     $this->error = 'Allowed response size exceeded';
                     if ($this->max_bodysize_abort) {
                         unset($this->connections[$connectionId]);
+                        set_time_limit($php_timeout);
                         return false;
                     } else {
                         break;
@@ -543,6 +563,7 @@ class HTTPClient {
 
         $this->_debug('response body',$this->resp_body);
         $this->redirect_count = 0;
+        set_time_limit($php_timeout);
         return true;
     }
 
diff --git a/inc/ZipLib.class.php b/inc/ZipLib.class.php
index cf89a40..3b519b5 100644
--- a/inc/ZipLib.class.php
+++ b/inc/ZipLib.class.php
@@ -187,7 +187,7 @@ class ZipLib {
     /**
      * Extract a zip file $zn to the $to directory
      */
-    function Extract ( $zn, $to, $index = Array(-1) ) {
+    function Extract ( $zn, $to, $index = Array(-1), $perm=0755 ) {
         if(!@is_dir($to)) $this->_mkdir($to);
         $ok = 0;
         $zip = @fopen($zn,'rb');
@@ -211,7 +211,7 @@ class ZipLib {
             @rewind($zip);
             fseek($zip, $header['offset']);
             if(in_array("-1",$index)||in_array($i,$index)){
-                $stat[$header['filename']]=$this->ExtractFile($header, $to, $zip);
+                $stat[$header['filename']]=$this->ExtractFile($header, $to, $zip, $perm);
             }
         }
         fclose($zip);
@@ -334,7 +334,7 @@ class ZipLib {
         return $centd;
     }
 
-    function ExtractFile($header,$to,$zip) {
+    function ExtractFile($header,$to,$zip,$perm) {
         $header = $this->readfileheader($zip, $header);
 
         if(substr($to,-1)!="/") $to.="/";
@@ -360,6 +360,7 @@ class ZipLib {
                 }
                 fclose($fp);
                 touch($to.$header['filename'], $header['mtime']);
+                chmod($to.$header['filename'], $perm);
 
             }else{
                 if (!is_dir(dirname($to.$header['filename']))) $this->_mkdir(dirname($to.$header['filename']));
@@ -404,6 +405,7 @@ class ZipLib {
                 gzclose($gzp);
 
                 touch($to.$header['filename'], $header['mtime']);
+                chmod($to.$header['filename'], $perm);
                 @unlink($to.$header['filename'].'.gz');
             }
         }
diff --git a/inc/cache.php b/inc/cache.php
index ff78e37..d61eea8 100644
--- a/inc/cache.php
+++ b/inc/cache.php
@@ -104,7 +104,7 @@ class cache {
      * @return  bool           true on success, false otherwise
      */
     function storeCache($data) {
-        return io_savefile($this->cache, $data);
+        return io_saveFile($this->cache, $data);
     }
 
     /**
@@ -265,6 +265,6 @@ class cache_instructions extends cache_parser {
     }
 
     function storeCache($instructions) {
-        return io_savefile($this->cache,serialize($instructions));
+        return io_saveFile($this->cache,serialize($instructions));
     }
 }
diff --git a/inc/common.php b/inc/common.php
index 0c769c5..6b40e6a 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -246,7 +246,7 @@ function buildAttributes($params,$skipempty=false){
     $white = false;
     foreach($params as $key => $val){
         if($key{0} == '_') continue;
-        if($val === '' && $skipempty) continue;
+        if($skipempty && (is_null($val) || $val === '')) continue;
         if($white) $url .= ' ';
 
         $url .= $key.'="';
diff --git a/inc/form.php b/inc/form.php
index e74c52c..520ce77 100644
--- a/inc/form.php
+++ b/inc/form.php
@@ -27,7 +27,7 @@ if(!defined('DOKU_INC')) die('meh.');
  */
 class Doku_Form {
 
-    // Form id attribute
+    // Form attributes
     var $params = array();
 
     // Draw a border around form fields.
@@ -176,11 +176,14 @@ class Doku_Form {
      * Gets the position of the first of a type of element.
      *
      * @param   string  $type   Element type to look for.
-     * @return  array   pseudo-element if found, false otherwise
+     * @param   string  $start   Position to begin searching from.
+     * @return  integer   Position of element if found, false otherwise
      * @author  Tom N Harris <tnharris [...]>
      */
-    function findElementByType($type) {
-        foreach ($this->_content as $pos=>$elem) {
+    function findElementByType($type, $start=0) {
+        $last = count($this->_content);
+        for ($pos=$start; $pos<$last; $pos++) {
+            $elem =& $this->_content[$pos];
             if (is_array($elem) && $elem['_elem'] == $type)
                 return $pos;
         }
@@ -193,11 +196,14 @@ class Doku_Form {
      * Gets the position of the element with an ID attribute.
      *
      * @param   string  $id     ID of the element to find.
-     * @return  array   pseudo-element if found, false otherwise
+     * @param   string  $start   Position to begin searching from.
+     * @return  integer   Position of element if found, false otherwise
      * @author  Tom N Harris <tnharris [...]>
      */
-    function findElementById($id) {
-        foreach ($this->_content as $pos=>$elem) {
+    function findElementById($id, $start=0) {
+        $last = count($this->_content);
+        for ($pos=$start; $pos<$last; $pos++) {
+            $elem =& $this->_content[$pos];
             if (is_array($elem) && isset($elem['id']) && $elem['id'] == $id)
                 return $pos;
         }
@@ -211,11 +217,14 @@ class Doku_Form {
      *
      * @param   string  $name   Attribute name.
      * @param   string  $value  Attribute value.
-     * @return  array   pseudo-element if found, false otherwise
+     * @param   string  $start   Position to begin searching from.
+     * @return  integer   Position of element if found, false otherwise
      * @author  Tom N Harris <tnharris [...]>
      */
-    function findElementByAttribute($name, $value) {
-        foreach ($this->_content as $pos=>$elem) {
+    function findElementByAttribute($name, $value, $start=0) {
+        $last = count($this->_content);
+        for ($pos=$start; $pos<$last; $pos++) {
+            $elem =& $this->_content[$pos];
             if (is_array($elem) && isset($elem[$name]) && $elem[$name] == $value)
                 return $pos;
         }
@@ -223,6 +232,16 @@ class Doku_Form {
     }
 
     /**
+     * findLastElement
+     *
+     * @return  integer   Position of last element
+     * @author  Tom N Harris <tnharris [...]>
+     */
+    function findLastElement() {
+        return empty($this->_content) ? false : (count($this->_content) - 1);
+    }
+
+    /**
      * getElementAt
      *
      * Returns a reference to the element at a position.
@@ -365,6 +384,7 @@ function form_makeCloseTag($tag) {
  */
 function form_makeWikiText($text, $attrs=array()) {
     $elem = array('_elem'=>'wikitext', '_text'=>$text,
+                        'name'=>'wikitext', 'id'=>'wiki__text',
                         'class'=>'edit', 'cols'=>'80', 'rows'=>'10');
     return array_merge($elem, $attrs);
 }
@@ -676,11 +696,7 @@ function form_hidden($attrs) {
  * @author  Tom N Harris <tnharris [...]>
  */
 function form_wikitext($attrs) {
-    // mandatory attributes
-    unset($attrs['name']);
-    unset($attrs['id']);
-    return '<textarea name="wikitext" id="wiki__text" '
-                 .buildAttributes($attrs,true).'>'.DOKU_LF
+    return '<textarea '.buildAttributes($attrs,true).'>'.DOKU_LF
                  .formText($attrs['_text'])
                  .'</textarea>';
 }
diff --git a/inc/init.php b/inc/init.php
index 14660b8..9ca4697 100644
--- a/inc/init.php
+++ b/inc/init.php
@@ -415,6 +415,9 @@ function getBaseURL($abs=null){
     //handle script in lib/plugins dir
     $dir = preg_replace('!lib/plugins/.*$!','',$dir);
 
+    //handle script in lib/tpl dir
+    $dir = preg_replace('!lib/tpl/.*$!','',$dir);
+
     //finish here for relative URLs
     if(!$abs) return $dir;
 
diff --git a/inc/parser/metadata.php b/inc/parser/metadata.php
index 136c375..4eb86ab 100644
--- a/inc/parser/metadata.php
+++ b/inc/parser/metadata.php
@@ -17,6 +17,7 @@ if ( !defined('DOKU_TAB') ) {
 }
 
 require_once DOKU_INC . 'inc/parser/renderer.php';
+require_once DOKU_INC . 'inc/utf8.php';
 
 /**
  * The Renderer
@@ -267,7 +268,13 @@ class Doku_Renderer_metadata extends Doku_Renderer {
   }
 
   function entity($entity){
-    if ($this->capture) $this->doc .= $entity;
+    if ($this->capture) {
+      if (array_key_exists($entity, $this->entities)) {
+        $this->doc .= utf8_unhtml($this->entities[$entity], HTML_ENTITIES);
+      } else {
+        $this->doc .= $entity;
+      }
+    }
   }
 
   function multiplyentity($x, $y){
@@ -374,6 +381,14 @@ class Doku_Renderer_metadata extends Doku_Renderer {
 
   function internalmedia($src, $title=NULL, $align=NULL, $width=NULL,
                          $height=NULL, $cache=NULL, $linking=NULL){
+    global $ID;
+
+    if (is_null($title))
+        $title = basename(noNS($src));
+
+    resolve_mediaid(getNS($ID),$src, $exists);
+    $this->meta['relation']['media'][$src] = $exists;
+
     if ($this->capture && $title) $this->doc .= '['.$title.']';
     $this->_firstimage($src);
   }
diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index bfa22d0..180d05b 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -854,6 +854,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
 
         require_once(DOKU_INC.'inc/FeedParser.php');
         $feed = new FeedParser();
+        $feed->set_cache_duration($params['refresh']);
         $feed->set_feed_url($url);
 
         //disable warning while fetching
diff --git a/inc/parserutils.php b/inc/parserutils.php
index 9384929..7214fb5 100644
--- a/inc/parserutils.php
+++ b/inc/parserutils.php
@@ -235,6 +235,7 @@ function p_cached_instructions($file,$cacheonly=false,$id='') {
  * @author Andreas Gohr <andi [...]>
  */
 function p_get_instructions($text){
+    trigger_event('PARSER_WIKITEXT_PREPROCESS', $text);
 
     $modes = p_get_parsermodes();
 
@@ -250,7 +251,6 @@ function p_get_instructions($text){
     }
 
     // Do the parsing
-    trigger_event('PARSER_WIKITEXT_PREPROCESS', $text);
     $p = $Parser->parse($text);
     //  dbg($p);
     return $p;
@@ -519,6 +519,7 @@ function p_render_metadata($id, $orig){
         $renderer = new Doku_Renderer_metadata();
         $renderer->meta =& $orig['current'];
         $renderer->persistent =& $orig['persistent'];
+        $renderer->entities = getEntities();
 
         // loop through the instructions
         foreach ($instructions as $instruction){
diff --git a/inc/template.php b/inc/template.php
index 024bf98..1036b00 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -693,7 +693,7 @@ function tpl_searchform($ajax=true,$autocomplete=true){
  *
  * @author Andreas Gohr <andi [...]>
  */
-function tpl_breadcrumbs($sep='&bull;'){
+function tpl_breadcrumbs($sep='&bull;', $rev=false){
     global $lang;
     global $conf;
 
@@ -704,15 +704,16 @@ function tpl_breadcrumbs($sep='&bull;'){
 
     //reverse crumborder in right-to-left mode, add RLM character to fix heb/eng display mixups
     if($lang['direction'] == 'rtl') {
-        $crumbs = array_reverse($crumbs,true);
+        if(!$rev) $crumbs = array_reverse($crumbs,true);
         $crumbs_sep = ' &#8207;<span class="bcsep">'.$sep.'</span>&#8207; ';
     } else {
+        if($rev) $crumbs = array_reverse($crumbs,true);
         $crumbs_sep = ' <span class="bcsep">'.$sep.'</span> ';
     }
 
     //render crumbs, highlight the last one
-    print '<span class="bchead">'.$lang['breadcrumb'].':</span>';
-    $last = count($crumbs);
+    if(!$rev) print '<span class="bchead">'.$lang['breadcrumb'].':</span>';
+    $last = ($rev)?1:count($crumbs);
     $i = 0;
     foreach ($crumbs as $id => $name){
         $i++;
@@ -721,6 +722,7 @@ function tpl_breadcrumbs($sep='&bull;'){
         tpl_link(wl($id),hsc($name),'class="breadcrumbs" title="'.$id.'"');
         if ($i == $last) print '</span>';
     }
+    if($rev) print '<span class="bchead">:'.$lang['breadcrumb'].'</span>';
     return true;
 }
 
@@ -766,9 +768,9 @@ function tpl_youarehere($sep=' &raquo; '){
 
     // print current page, skipping start page, skipping for namespace index
     resolve_pageid('',$page,$exists);
-    if(isset($page) && $page==$part.$parts[$i]) return;
+    if(isset($page) && $page==$part.$parts[$i]) return true;
     $page = $part.$parts[$i];
-    if($page == $conf['start']) return;
+    if($page == $conf['start']) return true;
     echo $sep;
     tpl_pagelink($page);
     return true;
diff --git a/inc/toolbar.php b/inc/toolbar.php
index 0217251..129e343 100644
--- a/inc/toolbar.php
+++ b/inc/toolbar.php
@@ -206,6 +206,7 @@ function toolbar_JSdefines($varname){
                 'title'  => $lang['qb_smileys'],
                 'icon'   => 'smiley.png',
                 'list'   => getSmileys(),
+                'mru'    => true,
                 'icobase'=> 'smileys',
                 'block'  => false
                ),
@@ -214,6 +215,7 @@ function toolbar_JSdefines($varname){
                 'title'  => $lang['qb_chars'],
                 'icon'   => 'chars.png',
                 'list'   => explode(' ','À à Á á  â à ã Ä ä Ǎ ǎ Ă ă Å å Ā ā Ą ą Æ æ Ć ć Ç ç Č č Ĉ ĉ Ċ ċ Ð đ ð Ď ď È è É é Ê ê Ë ë Ě ě Ē ē Ė ė Ę ę Ģ ģ Ĝ ĝ Ğ ğ Ġ ġ Ĥ ĥ Ì ì Í í Î î Ï ï Ǐ ǐ Ī ī İ ı Į į Ĵ ĵ Ķ ķ Ĺ ĺ Ļ ļ Ľ ľ Ł ł Ŀ ŀ Ń ń Ñ ñ Ņ ņ Ň ň Ò ò Ó ó Ô ô Õ õ Ö ö Ǒ ǒ Ō ō Ő ő Œ œ Ø ø Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ş ş Š š Ŝ ŝ Ţ ţ Ť ť Ù ù Ú ú Û û Ü ü Ǔ ǔ Ŭ ŭ Ū ū Ů ů ǖ ǘ ǚ ǜ Ų ų Ű ű Ŵ ŵ Ý ý Ÿ ÿ Ŷ ŷ Ź ź Ž ž Ż ż Þ þ ß Ħ ħ ¿ ¡ ¢ £ ¤ ¥ € ¦ § ª ¬ ¯ ° ± ÷ ‰ ¼ ½ ¾ ¹ ² ³ µ ¶ † ‡ · • º ∀ ∂ ∃ Ə ə ∅ ∇ ∈ ∉ ∋ ∏ ∑ ‾ − ∗ × ⁄ √ ∝ ∞ ∠ ∧ ∨ ∩ ∪ ∫ ∴ ∼ ≅ ≈ ≠ ≡ ≤ ≥ ⊂ ⊃ ⊄ ⊆ ⊇ ⊕ ⊗ ⊥ ⋅ ◊ ℘ ℑ ℜ ℵ ♠ ♣ ♥ ♦ α β Γ γ Δ δ ε ζ η Θ θ ι κ Λ λ μ Ξ ξ Π π ρ Σ σ Τ τ υ Φ φ χ Ψ ψ Ω ω ★ ☆ ☎ ☚ ☛ ☜ ☝ ☞ ☟ ☹ ☺ ✔ ✘ „ “ ” ‚ ‘ ’ « » ‹ › — – … ← ↑ → ↓ ↔ ⇐ ⇑ ⇒ ⇓ ⇔ © ™ ® ′ ″ [ ] { } ~ ( ) % § $ # | @'),
+                'mru'    => true,
                 'block'  => false
                ),
           array(
diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php
index 143d40f..df00e6f 100644
--- a/lib/exe/fetch.php
+++ b/lib/exe/fetch.php
@@ -30,6 +30,9 @@
     $MIME = 'application/octet-stream';
     $DL   = true;
   }
+  $ACT    = (isset($_REQUEST['do'])) ? $_REQUEST['do'] : 'fetch';
+  if($ACT == 'download')
+    $DL = true;
 
   // check for permissions, preconditions and cache external files
   list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV);
diff --git a/lib/exe/indexer.php b/lib/exe/indexer.php
index 95e2af0..3053a6c 100644
--- a/lib/exe/indexer.php
+++ b/lib/exe/indexer.php
@@ -35,6 +35,7 @@ if ($evt->advise_before()) {
   sendDigest() or
   runTrimRecentChanges() or
   runTrimRecentChanges(true) or
+  cleanCache() or
   $evt->advise_after();
 }
 if($defer) sendGIF();
@@ -246,6 +247,40 @@ function sendDigest() {
 }
 
 /**
+ * Purge cache of very old files
+ * Files are removed after $conf[cachetime]*60 (or after 6 days, whichever is longer)
+ *
+ * @author Tom N Harris <tnharris [...]>
+ */
+function cleanCache(){
+    global $conf;
+    print "cleanCache(): started".NL;
+    if($conf['nopurgecache']) return false;
+    $maxage = max($conf['cachetime']*60,518400);
+    // use the purgefile time to not waste time cycling the cache when nothing has changed.
+    $maxtime = @filemtime($conf['cachedir'].'/purgefile') - $maxage;
+    if ($maxtime <= 0) return false;
+
+    require_once(DOKU_INC.'inc/search.php');
+    $files = array();
+    search($files, $conf['cachedir'], 'cache_cleaner', array('time'=>$maxtime));
+    print "cleanCache(): purged ".count($files)." files".NL;
+    print "cleanCache(): finished".NL;
+    return !empty($files);
+}
+function cache_cleaner(&$data,$base,$file,$type,$lvl,$opts){
+    // Only delete files in subdirectories. Anything in $conf[cachedir]
+    // (purgefile, stats, etc.) is safe
+    if ($type == 'f' && $lvl > 1) {
+        if (@filemtime($base.'/'.$file) < $opts['time']) {
+            @unlink($base.'/'.$file);
+            $data[] = $file;
+        }
+    }
+    return true;
+}
+
+/**
  * Just send a 1x1 pixel blank gif to the browser
  *
  * @author Andreas Gohr <andi [...]>
diff --git a/lib/images/fileicons/ac3.png b/lib/images/fileicons/ac3.png
new file mode 100644
index 0000000..92ca7c1
Binary files /dev/null and b/lib/images/fileicons/ac3.png differ
diff --git a/lib/images/fileicons/aiff.png b/lib/images/fileicons/aiff.png
new file mode 100644
index 0000000..d43ae0b
Binary files /dev/null and b/lib/images/fileicons/aiff.png differ
diff --git a/lib/images/fileicons/asf.png b/lib/images/fileicons/asf.png
new file mode 100644
index 0000000..ebe2549
Binary files /dev/null and b/lib/images/fileicons/asf.png differ
diff --git a/lib/images/fileicons/avi.png b/lib/images/fileicons/avi.png
new file mode 100644
index 0000000..b279491
Binary files /dev/null and b/lib/images/fileicons/avi.png differ
diff --git a/lib/images/fileicons/blend.png b/lib/images/fileicons/blend.png
new file mode 100644
index 0000000..1c8fed8
Binary files /dev/null and b/lib/images/fileicons/blend.png differ
diff --git a/lib/images/fileicons/cfg.png b/lib/images/fileicons/cfg.png
new file mode 100644
index 0000000..ddffe6f
Binary files /dev/null and b/lib/images/fileicons/cfg.png differ
diff --git a/lib/images/fileicons/divx.png b/lib/images/fileicons/divx.png
new file mode 100644
index 0000000..86846e8
Binary files /dev/null and b/lib/images/fileicons/divx.png differ
diff --git a/lib/images/fileicons/eps.png b/lib/images/fileicons/eps.png
new file mode 100644
index 0000000..c51c763
Binary files /dev/null and b/lib/images/fileicons/eps.png differ
diff --git a/lib/images/fileicons/ini.png b/lib/images/fileicons/ini.png
new file mode 100644
index 0000000..ddffe6f
Binary files /dev/null and b/lib/images/fileicons/ini.png differ
diff --git a/lib/images/fileicons/mov.png b/lib/images/fileicons/mov.png
new file mode 100644
index 0000000..bbd6b60
Binary files /dev/null and b/lib/images/fileicons/mov.png differ
diff --git a/lib/images/fileicons/mpeg.png b/lib/images/fileicons/mpeg.png
new file mode 100644
index 0000000..7f26d24
Binary files /dev/null and b/lib/images/fileicons/mpeg.png differ
diff --git a/lib/images/fileicons/mpg.png b/lib/images/fileicons/mpg.png
new file mode 100644
index 0000000..7f26d24
Binary files /dev/null and b/lib/images/fileicons/mpg.png differ
diff --git a/lib/images/fileicons/py.png b/lib/images/fileicons/py.png
index 15a727c..96a0eab 100644
Binary files a/lib/images/fileicons/py.png and b/lib/images/fileicons/py.png differ
diff --git a/lib/images/fileicons/rm.png b/lib/images/fileicons/rm.png
new file mode 100644
index 0000000..162cc01
Binary files /dev/null and b/lib/images/fileicons/rm.png differ
diff --git a/lib/images/fileicons/tbz2.png b/lib/images/fileicons/tbz2.png
new file mode 100644
index 0000000..d48cae0
Binary files /dev/null and b/lib/images/fileicons/tbz2.png differ
diff --git a/lib/images/fileicons/tex.png b/lib/images/fileicons/tex.png
new file mode 100644
index 0000000..03142dc
Binary files /dev/null and b/lib/images/fileicons/tex.png differ
diff --git a/lib/images/fileicons/wma.png b/lib/images/fileicons/wma.png
new file mode 100644
index 0000000..50e6e40
Binary files /dev/null and b/lib/images/fileicons/wma.png differ
diff --git a/lib/images/fileicons/wmv.png b/lib/images/fileicons/wmv.png
new file mode 100644
index 0000000..8235f83
Binary files /dev/null and b/lib/images/fileicons/wmv.png differ
diff --git a/lib/images/fileicons/xhtml.png b/lib/images/fileicons/xhtml.png
new file mode 100644
index 0000000..672cbce
Binary files /dev/null and b/lib/images/fileicons/xhtml.png differ
diff --git a/lib/images/fileicons/xvid.png b/lib/images/fileicons/xvid.png
new file mode 100644
index 0000000..a293462
Binary files /dev/null and b/lib/images/fileicons/xvid.png differ
diff --git a/lib/images/interwiki/anidb.png b/lib/images/interwiki/anidb.png
new file mode 100644
index 0000000..25563ba
Binary files /dev/null and b/lib/images/interwiki/anidb.png differ
diff --git a/lib/images/interwiki/ann.png b/lib/images/interwiki/ann.png
new file mode 100644
index 0000000..0559da1
Binary files /dev/null and b/lib/images/interwiki/ann.png differ
diff --git a/lib/images/interwiki/ttlg.gif b/lib/images/interwiki/ttlg.gif
new file mode 100644
index 0000000..444ec5b
Binary files /dev/null and b/lib/images/interwiki/ttlg.gif differ
diff --git a/lib/images/interwiki/urban.gif b/lib/images/interwiki/urban.gif
new file mode 100644
index 0000000..281d19f
Binary files /dev/null and b/lib/images/interwiki/urban.gif differ
diff --git a/lib/images/smileys/icon_ss2.png b/lib/images/smileys/icon_ss2.png
new file mode 100644
index 0000000..ab71557
Binary files /dev/null and b/lib/images/smileys/icon_ss2.png differ
diff --git a/lib/images/smileys/icon_t1.png b/lib/images/smileys/icon_t1.png
new file mode 100644
index 0000000..0accbc4
Binary files /dev/null and b/lib/images/smileys/icon_t1.png differ
diff --git a/lib/images/smileys/icon_t2.png b/lib/images/smileys/icon_t2.png
new file mode 100644
index 0000000..3e1eaa9
Binary files /dev/null and b/lib/images/smileys/icon_t2.png differ
diff --git a/lib/plugins/action.php b/lib/plugins/action.php
index 885bd7c..e047017 100644
--- a/lib/plugins/action.php
+++ b/lib/plugins/action.php
@@ -17,7 +17,7 @@ class DokuWiki_Action_Plugin extends DokuWiki_Plugin {
      /**
       * Registers a callback function for a given event
       */
-      function register($controller) {
+      function register(&$controller) {
         trigger_error('register() not implemented in '.get_class($this), E_USER_WARNING);
       }
 }
diff --git a/lib/plugins/plugin/classes/ap_download.class.php b/lib/plugins/plugin/classes/ap_download.class.php
index b2571f6..d951a95 100644
--- a/lib/plugins/plugin/classes/ap_download.class.php
+++ b/lib/plugins/plugin/classes/ap_download.class.php
@@ -215,7 +215,7 @@ class ap_download extends ap_manage {
                 }
                 return false;
             }
-            $ok = $tar->Extract(TarLib::FULL_ARCHIVE, $target, '', 0777);
+            $ok = $tar->Extract(TarLib::FULL_ARCHIVE, $target, '', $conf['fperm']);
 
             if($ok<1){
                 if($conf['allowdebug']){
@@ -227,7 +227,7 @@ class ap_download extends ap_manage {
         } else if ($ext == 'zip') {
 
             $zip = new ZipLib();
-            $ok = $zip->Extract($file, $target);
+            $ok = $zip->Extract($file, $target, -1, $conf['fperm']);
 
             // FIXME sort something out for handling zip error messages meaningfully
             return ($ok==-1?false:true);
diff --git a/lib/scripts/edit.js b/lib/scripts/edit.js
index 33a8f61..ba02ca1 100644
--- a/lib/scripts/edit.js
+++ b/lib/scripts/edit.js
@@ -74,7 +74,7 @@ function createPicker(id,props,edid){
     function $makebutton(title) {
         var $btn = jQuery(document.createElement('button'))
             .addClass('pickerbutton').attr('title', title)
-            .bind('click', bind(pickerInsert, title, edid))
+            .bind('click', bind(pickerInsert, title, edid, props['mru']))
             .appendTo($picker);
         return $btn;
     }
@@ -113,9 +113,28 @@ function createPicker(id,props,edid){
  *
  * @author Andreas Gohr <andi [...]>
  */
-function pickerInsert(text,edid){
+function pickerInsert(text,edid,mru){
     insertAtCarret(edid,text);
     pickerClose();
+    if(mru){
+        var $clicked = jQuery(this);
+        var $picker = $clicked.parent();
+        if (!$clicked.hasClass('mru5')) {
+            $clicked.detach();
+            $picker.find('button').each(function () {
+                if (this.className.match(/ mru(\d)/)) {
+                    var nr = this.className.match(/ mru(\d)/)[1];
+                    this.className = this.className.replace(' mru'+nr, (nr>1)?(' mru'+(nr-1)):'');
+                }
+            });
+            if(this.className.match(/ mru(\d)/)){
+                this.className = this.className.replace(/ mru(\d)/,' mru5');
+            }else{
+                this.className += ' mru5';
+            }
+            $picker.prepend($clicked);
+        }
+    }
 }
 
 /**
diff --git a/lib/styles/feed.css b/lib/styles/feed.css
index 44b72d7..f5c9e50 100644
--- a/lib/styles/feed.css
+++ b/lib/styles/feed.css
@@ -1,5 +1,6 @@
 rss channel, feed, RDF {
-  font: 80% "Lucida Grande", Verdana, Lucida, Helvetica, Arial, sans-serif;
+  font-size: 80%;
+  font-family: __font_text__;
   background-color: __background__;
   color: __text__;
   margin: 0;
@@ -53,7 +54,8 @@ item title, entry title {
 item pubDate, entry modified, item date {
   display: inline;
   color: __text_neu__;
-  font: 1em trebuchet ms, arial, helvetica, sans-serif;
+  font-size: 1em;
+  font-family: __font_meta__;
 }
 
 item description, entry summary {
diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css
index a94f814..a6443e2 100644
--- a/lib/tpl/default/design.css
+++ b/lib/tpl/default/design.css
@@ -820,9 +820,16 @@ button.pickerbutton {
   border: 0;
   background-color: transparent;
   font-size: 80%;
+  font-family: "Lucida Grande", Verdana, Lucida, Helvetica, Arial, sans-serif;
   cursor: pointer;
 }
 
+button.mru5 { font-size: 150%; }
+button.mru4 { font-size: 140%; }
+button.mru3 { font-size: 130%; }
+button.mru2 { font-size: 120%; }
+button.mru1 { font-size: 110%; }
+
 /* ---------------  Image Details  ----------------- */
 
 div.dokuwiki div.img_big {
diff --git a/lib/tpl/default/style.ini b/lib/tpl/default/style.ini
index 7d27381..2ebfdb4 100644
--- a/lib/tpl/default/style.ini
+++ b/lib/tpl/default/style.ini
@@ -55,6 +55,10 @@ __missing__   = "#f30"
 __highlight__ = "#ff9"
 
 
+__font_text__ = "'Lucida Grande', Verdana, Lucida, Helvetica, Arial, sans-serif"
+__font_meta__ = "trebuchet ms, arial, helvetica, sans-serif"
+__font_edit__ = "monospace"
+
 ;--------------------------------------------------------------------------
 ;------ for keeping old templates and plugins compatible to the old pattern
 ; (to be deleted at the next or after next release)