1<?php /**
2 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
3 * @author     Michael Klier <chi@chimeric.de>
4 * @author     Myron Turner <turnermm02@shaw.ca>
5 */
6if(!defined('DOKU_INC')) die();
7
8if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
9require_once(DOKU_PLUGIN.'action.php');
10
11/**
12 * All DokuWiki plugins to extend the parser/rendering mechanism
13 * need to inherit from this class
14 */
15class action_plugin_snippets extends DokuWiki_Action_Plugin {
16    private $metafn;
17    private $helper;
18    /**
19     * Register callbacks
20     */
21    function register(Doku_Event_Handler $controller) {
22        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call');
23        $controller->register_hook('IO_WIKIPAGE_READ', 'AFTER', $this, 'handle_wiki_read');
24        $controller->register_hook('TPL_ACT_RENDER', 'AFTER', $this, 'handle_content_display');
25        $controller->register_hook('IO_WIKIPAGE_WRITE', 'AFTER', $this, 'handle_wiki_write',array('after'=>true,'before'=>false));
26        $controller->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'handle_wiki_write', array('before'=>true, 'after'=>false));
27        $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'handle_dw_started');
28        $controller->register_hook('COMMON_PAGETPL_LOAD', 'AFTER', $this, 'handle_template');
29        $controller->register_hook('HTML_SHOWREV_OUTPUT', 'BEFORE', $this, 'handle_revoutput', array('before'));
30    }
31
32        /**
33     *  Sets up database file for pages requiring updates
34     *  @author Myron Turner<turnermm02@shaw.ca>
35    */
36    function __construct() {
37        $this->metafn = metaFN('snippets_upd','.ser');
38        if(!file_exists($this->metafn)) {
39            $ar = array('snip'=>array(), 'doc'=>array());
40            io_saveFile($this->metafn,serialize($ar));
41        }
42        $this->helper = $this->loadHelper('snippets');
43    }
44
45     function handle_revoutput(Doku_Event $event, $param){
46
47      global $INFO;
48     $metafn = $this->helper->getMetaFileName();
49     $snip_data=unserialize(io_readFile($this->metafn,false));
50     if(!array_key_exists($INFO['id'],$snip_data['doc'])) return;
51
52     $event->preventDefault();
53     $old_rev=$this->getConf('old_revisions')?'on':'off';
54     $onoff = get_doku_pref('snippets_old_rev', $old_rev);
55     $ON_CHKD="";
56     $OFF_CHKD = '';
57     $msg_2 = "";
58     $yesno = "";
59     if($onoff == 'on') {
60         $ON_CHKD='CHECKED';
61         $yesno =$this->getLang('no');
62         $msg_2 = $this->getLang('msg_2');  // 'Any outdated snippets have already been inserted. ';
63     }
64     else {
65         $OFF_CHKD='CHECKED';
66         $yesno =$this->getLang('yes');
67     }
68     $msg = p_locale_xhtml("showrev");
69     $n = preg_match('/strong>(.*?)<\/strong/',$msg, $matches);
70     $msg = str_replace('!', '.',$matches[1]) . ' ';
71     echo $msg  .$this->getLang('oldrev_msg');
72
73     echo ' ' . $msg_2 . '<br /><span class="yesnosnippet" id="yesnosnippet">' . $this->getLang('replace') . '</span>'
74       . '<input type="radio" name="snippetOldRevwhich" ' . $ON_CHKD . ' value="on"  onchange="snippets_InsertIntOldRev(this.value);" />Yes&nbsp;'
75       .'<input type="radio"  onchange="snippets_InsertIntOldRev(this.value);" '  . $OFF_CHKD . ' name="snippetOldRevwhich" value="off" />No<br/>';
76     echo  "<br /><hr />" ;
77
78     }
79
80    function handle_dw_started(Doku_Event $event, $param) {
81        global $JSINFO;
82        if($this->getConf('snips_updatable')) {
83            $JSINFO['updatable'] = 1;
84        }
85        else $JSINFO['updatable'] = 0;
86
87        if($this->getConf('userreplace')) {
88            $JSINFO['userreplace'] = 1;
89        }
90      else $JSINFO['userreplace'] = 0;
91
92      $JSINFO['default_macro_string'] = $this->getConf('default_macro_string') ? $this->getConf('default_macro_string') : "";
93    }
94
95    function handle_template(Doku_Event $event, $param) {
96        $file = get_doku_pref('qs','');
97        $event->data['tpl'] = preg_replace('/<snippet>.*?<\/snippet>/s',"",$event->data['tpl']);
98
99         $stringvars =   // from newpagtemplate userreplace
100             array_map(function($v) { return explode(",",$v,2); },
101                 explode(';',$_REQUEST['macros']));
102         foreach($stringvars as $value) {
103             $event->data['tpl'] = str_replace(trim($value[0]),hsc(trim($value[1])),$event->data['tpl']);
104	    }
105
106        if(!$file) return;
107        $page_id = $file;
108        $file = noNS($file);
109        $page = cleanID($file);
110        if($this->getConf('prettytitles')) {
111            $title= str_replace('_',' ',$page);
112        }
113        else {
114           $title = $page;
115        }
116        $event->data['tpl'] = str_replace(array(
117                              '#ID#',
118                              '#NS#',
119                              '#FILE#',
120                              '#!FILE#',
121                              '#!FILE!#',
122                              '#PAGE#',
123                              '#!PAGE#',
124                              '#!!PAGE#',
125                              '#!PAGE!#',
126
127                           ),
128                           array(
129                              $page_id,
130                              getNS($page_id),
131                              $file,
132                              utf8_ucfirst($file),
133                              utf8_strtoupper($file),
134                              $page,
135                              utf8_ucfirst($title),
136                              utf8_ucwords($title),
137                              utf8_strtoupper($title)
138                              ),
139                              $event->data['tpl']);
140
141    }
142     /**
143     * Replaces outdated snippets with updated versions
144     * Is capable of replacing more than one snippet in a page
145     *
146     * @author Myron Turner <turnermm02@shaw.ca>
147     */
148     function handle_wiki_read(Doku_Event $event,$param) {
149
150      $force_old = $this->getConf('old_revisions');
151      $default = $force_old ? 'on' : 'off';
152      $default = get_doku_pref('snippets_old_rev', $default);
153      if($default == 'on') {
154          $force_old = true;
155      }
156      else $force_old = false;
157
158       if($event->data[3] && !$force_old) return;
159      $force_old = true;
160       if($event->data[1]) {
161         $page_id = ltrim($event->data[1] . ':' . $event->data[2], ":");
162        }
163        else {
164           $page_id = $event->data[2];
165        }
166       $this->helper->insertSnippet($event->result, $page_id, $force_old);
167     }
168
169
170
171    /**
172      *  Update the array of snippet timestamps in meta files of pages where snippets are inserted
173      *
174      * @author Myron Turner <turnermm02@shaw.ca>
175     */
176    function handle_wiki_write(Doku_Event $event, $param) {
177
178       if(! $event->result && $param['after']) return;  //write fail
179
180       if($event->data[1]) {
181        $page_id = ltrim($event->data[1] . ':' . $event->data[2], ":");
182        }
183        else {
184           $page_id = $event->data[2];
185        }
186
187        $snip_data=unserialize(io_readFile($this->metafn,false));
188
189        if(!array_key_exists($page_id,$snip_data['doc']))  return;
190        $snippets = $snip_data['doc'][$page_id];
191        if($param['before']) {
192           preg_match_all("/~~SNIPPET_C~~(.*?)~~/",$event->data[0][1],$matches);
193          $intersect = array_intersect($snippets,$matches[1]);
194          if(!empty($intersect)) {
195              $snip_data['doc'][$page_id] = $intersect;
196               io_saveFile($this->metafn,serialize($snip_data));
197          }
198          return;
199        }
200
201
202        if(preg_match('#data/pages/#', $event->data[0][0])) {  //make sure this is data/page not meta/attic save
203            foreach($snippets as $snip) {
204             $retv =  $this->helper->updateMetaTime($page_id,$snip) ;
205           //  msg($retv);
206            }
207        }
208
209
210    }
211    /*
212      *  After a snippet has been revised, this outputs table of links on the snippet page itemizing those pages where this
213      * snippet is inserted.  The links implement an ajax call to exe/update.php where the snippets can be updated
214      * and the timestamps of the updated snippets revised in the metafile of the pages where snippet is inserted
215   */
216    function handle_content_display(Doku_Event $event, $param) {
217        global $INFO;
218        $snipid = $INFO['id'];
219        $table = array();
220
221        $snip_data=unserialize(io_readFile($this->metafn,false));
222        if(!array_key_exists($snipid,$snip_data['snip'])) return;
223
224        $snip_time= filemtime(wikiFN($snipid));
225
226        $table[] = "<div id='snippet_update_table'>\nSnippet date: " . date('r',$snip_time) .'<br />';
227        $table[]='<form><input type="checkbox" name="prune" value="prune" id="snip_prune" checked><span title =" '. $this->getLang('refresh_title') . '"> ' . $this->getLang('refresh') .'</span></form><br />';
228        $table[] ="<table>\n";
229        $table[] ='<tr><th>Page date<th>' . $this->getLang('click_to_update') .'</tr>';
230        $bounding_rows = count($table);
231        $page_ids = $snip_data['snip'][$snipid];
232        foreach($page_ids as $pid) {
233           $page_time= filemtime(wikiFN($pid));
234            if($snip_time > $page_time) {
235              $span = str_replace(':','_',$pid);
236              $table[]= "<tr><td>" . date('r',$page_time) . '<td><a href="javascript:update_snippets(\''.$pid .'\');"  id="' .$span . '">' .$pid .'</a><tr />';
237            }
238        }
239        $table[]='</table></div>';
240        $table[]='<p><span id="snip_updates_but" style="color:#2b73b7;">' .$this->getLang('hide_table') . '</span></p>';
241
242        if(count($table) > $bounding_rows+2) {
243            foreach($table as $line) {
244                print $line  . NL;
245        }
246        }
247    }
248    /**
249     * Handles the AJAX calls
250     *
251     * @author Michael Klier <chi@chimeric.de>
252     * @author Myron Turner <turnermm02@shaw.ca>
253     */
254    function handle_ajax_call(Doku_Event $event, $param) {
255        global $lang;
256
257        if($event->data == 'snippet_preview' or $event->data == 'snippet_insert'  or $event->data == 'snippet_update' ) {
258            $event->preventDefault();
259            $event->stopPropagation();
260            $id = cleanID($_REQUEST['id']);
261            if(page_exists($id)) {
262                if($event->data == 'snippet_preview') {
263                    if(auth_quickaclcheck($id) >= AUTH_READ) {
264                        print p_wiki_xhtml($id);
265                    } else {
266                        print p_locale_xhtml('denied');
267                    }
268                } elseif($event->data == 'snippet_insert' || $event->data == 'snippet_update') {
269                    $template = false;
270                    if(preg_match("/templ_|templ:/",$id)) $template = true;
271                    if(auth_quickaclcheck($id) >= AUTH_READ) {
272                        if($event->data == 'snippet_update'  && ! $template) {  // templates are permanent
273                          $tm = time();
274                           print "\n~~SNIPPET_O${tm}~~$id~~\n";
275                        }
276                        print "\n\n"; // always start on a new line (just to be safe)
277                        if($template) {
278                             $tpl = pageTemplate($id);
279                              if($this->getConf('skip_unset_macros')) {
280                                  $tpl = preg_replace("/@\w+@/m","",$tpl);
281                              }
282                              print($tpl);
283
284                        }
285                        else print trim(preg_replace('/<snippet>.*?<\/snippet>/s', '', io_readFile(wikiFN($id))));
286
287                        if($event->data == 'snippet_update' && ! $template) {
288                             print "\n\n~~SNIPPET_C~~$id~~\n";
289                             $curpage = cleanID($_REQUEST['curpage']);   // $curpage is page into which snippet is being inserted
290                             $snip_data=unserialize(io_readFile($this->metafn,false));
291                             if(!array_key_exists($curpage,$snip_data['doc'])) {   // insert $curpage into doc array
292                                 $snip_data['doc'][$curpage] = array($id);                // and put current snippet into its list of snippets
293                            }
294                             elseif(!in_array($id,$snip_data['doc'][$curpage])) {
295                                      // if already in doc array just  put current snippet in its list of snippets
296                                  $snip_data['doc'][$curpage][]= $id;
297                             }
298                             if(!array_key_exists($id,$snip_data['snip'])) {  //   do the same as above but in reverse for snippets
299                                 $snip_data['snip'][$id] = array($curpage);
300                             }
301                             elseif(!in_array($curpage,$snip_data['snip'][$id])) {
302                                $snip_data['snip'][$id][] = $curpage;
303                             }
304                             io_saveFile($this->metafn,serialize($snip_data));
305                        }
306                    }
307                }
308            }
309        }
310    }
311}
312// vim:ts=4:sw=4:et:enc=utf-8:
313