1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Esther Brunner <wikidesign@gmail.com>
5 */
6
7class action_plugin_blog extends DokuWiki_Action_Plugin {
8
9    /**
10     * register the eventhandlers
11     * @param Doku_Event_Handler $contr
12     */
13    function register(Doku_Event_Handler $contr) {
14        $contr->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_act_preprocess', array());
15        $contr->register_hook('FEED_ITEM_ADD', 'BEFORE', $this, 'handle_feed_item');
16        $contr->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, 'handle_cache');
17    }
18
19    /**
20     * Checks if 'newentry' was given as action, if so we
21     * do handle the event our self and no further checking takes place
22     * @param Doku_Event $event
23     * @param $param
24     */
25    function handle_act_preprocess(Doku_Event $event, $param) {
26        if ($event->data != 'newentry') return; // nothing to do for us
27
28        $event->data = $this->_handle_newEntry($event);
29    }
30
31    /**
32     * Removes draft entries from feeds
33     *
34     * @param Doku_Event $event
35     * @param $param
36     *
37     * @author Michael Klier <chi@chimeric.de>
38     */
39    function handle_feed_item(Doku_Event $event, $param) {
40        global $conf;
41
42        $url = parse_url($event->data['item']->link);
43        $base_url = getBaseURL();
44
45        // determine page id by rewrite mode
46        switch($conf['userewrite']) {
47            case 0:
48                preg_match('#id=([^&]*)#', $url['query'], $match);
49                if($base_url != '/') {
50                    $id = cleanID(str_replace($base_url, '', $match[1]));
51                } else {
52                    $id = cleanID($match[1]);
53                }
54                break;
55
56            case 1:
57                if($base_url != '/') {
58                    $id = cleanID(str_replace('/',':',str_replace($base_url, '', $url['path'])));
59                } else {
60                    $id = cleanID(str_replace('/',':', $url['path']));
61                }
62                break;
63
64            case 2:
65                preg_match('#doku.php/([^&]*)#', $url['path'], $match);
66                if($base_url != '/') {
67                    $id = cleanID(str_replace($base_url, '', $match[1]));
68                } else {
69                    $id = cleanID($match[1]);
70                }
71                break;
72        }
73
74        // don't add drafts to the feed
75        if(p_get_metadata($id, 'type') == 'draft') {
76            $event->preventDefault();
77            return;
78        }
79    }
80
81    /**
82     * Creates a new entry page
83     *
84     * @param Doku_Event $event
85     * @return string
86     */
87    function _handle_newEntry(Doku_Event $event) {
88        global $ID, $INFO;
89
90        $ns    = cleanID($_REQUEST['ns']);
91        $ID    = $this->_newEntryID($ns, $_REQUEST['title']);
92        $INFO  = pageinfo();
93
94        // check if we are allowed to create this file
95        if ($INFO['perm'] >= AUTH_CREATE) {
96
97            // prepare the new thread file with default stuff
98            if (!@file_exists($INFO['filepath'])) {
99
100                //check if locked by anyone - if not lock for my self
101                if ($INFO['locked']) return 'locked';
102                else lock($ID);
103
104                global $TEXT;
105
106                $TEXT = pageTemplate($ID);
107                if (!$TEXT) {
108                    // if there is no page template, load our custom one
109                    $TEXT  = io_readFile(DOKU_PLUGIN.'blog/_template.txt');
110                }
111
112                $data = array('id' => $ID, 'ns' => $ns, 'title' => $_REQUEST['title']);
113                // Apply replacements regardless if they have already been applied by DokuWiki in order to
114                // make custom replacements like @TITLE@ available in standard page templates.
115                $TEXT = $this->_pageTemplate($TEXT, $data);
116
117                return 'preview';
118            } else {
119                return 'edit';
120            }
121        } else {
122            return 'show';
123        }
124    }
125
126    /**
127     * Adapted version of pageTemplate() function
128     *
129     * @param $text
130     * @param $data
131     * @return string|string[]
132     */
133    function _pageTemplate($text, $data) {
134        global $conf, $INFO;
135
136        $id   = $data['id'];
137        $user = $_SERVER['REMOTE_USER'];
138
139        // standard replacements
140        $replace = array(
141                '@ID@'   => $id,
142                '@NS@'   => $data['ns'],
143                '@PAGE@' => strtr(noNS($id),'_',' '),
144                '@USER@' => $user,
145                '@NAME@' => $INFO['userinfo']['name'],
146                '@MAIL@' => $INFO['userinfo']['mail'],
147                '@DATE@' => strftime($conf['dformat']),
148                );
149
150        // additional replacements
151        $replace['@TITLE@'] = $data['title'];
152
153        // tag if tag plugin is available
154        if ((@file_exists(DOKU_PLUGIN.'tag/syntax/tag.php'))
155                && (!plugin_isdisabled('tag'))) {
156            $replace['@TAG@'] = "\n\n{{tag>}}";
157        } else {
158            $replace['@TAG@'] = '';
159        }
160
161        // discussion if discussion plugin is available
162        if ((@file_exists(DOKU_PLUGIN.'discussion/syntax/comments.php'))
163                && (!plugin_isdisabled('discussion'))) {
164            $replace['@DISCUSSION@'] = "~~DISCUSSION~~";
165        } else {
166            $replace['@DISCUSSION@'] = '';
167        }
168
169        // linkbacks if linkback plugin is available
170        if ((@file_exists(DOKU_PLUGIN.'linkback/syntax.php'))
171                && (!plugin_isdisabled('linkback'))) {
172            $replace['@LINKBACK@'] = "~~LINKBACK~~";
173        } else {
174            $replace['@LINKBACK@'] = '';
175        }
176
177        // do the replace
178        return str_replace(array_keys($replace), array_values($replace), $text);
179    }
180
181    /**
182     * Returns the ID of a new entry based on its namespace, title and the date prefix
183     *
184     * @param $ns
185     * @param $title
186     * @return mixed|string|string[]|null
187     *
188     * @author  Michael Arlt <michael.arlt@sk-chwanstetten.de>
189     * @author  Esther Brunner <wikidesign@gmail.com>
190     */
191    function _newEntryID($ns, $title) {
192        $dateprefix  = $this->getConf('dateprefix');
193        if (substr($dateprefix, 0, 1) == '<') {
194            // <9?%y1-%y2:%d.%m._   ->  05-06:31.08._ | 06-07:01.09._
195            list($newmonth, $dateprefix) = explode('?', substr($dateprefix, 1));
196            if (intval(strftime("%m")) < intval($newmonth)) {
197                $longyear2 = strftime("%Y");
198                $longyear1 = $longyear2 - 1;
199            } else {
200                $longyear1 = strftime("%Y");
201                $longyear2 = $longyear1 + 1;
202            }
203            $shortyear1 = substr($longyear1, 2);
204            $shortyear2 = substr($longyear2, 2);
205            $dateprefix = str_replace(
206                    array('%Y1', '%Y2', '%y1', '%y2'),
207                    array($longyear1, $longyear2, $shortyear1, $shortyear2),
208                    $dateprefix
209                    );
210        }
211        $pre = strftime($dateprefix);
212        $title = str_replace([':', ';', '#', '&', '%', '/', '\\', '?'], '', $title);
213        return cleanID(($ns ? $ns.':' : '').$pre.$title);
214    }
215
216    /**
217     * Expire the renderer cache of archive pages whenever a page is updated or a comment or linkback is added
218     *
219     * @param Doku_Event $event
220     * @param $params
221     *
222     * @author Michael Hamann <michael@content-space.de>
223     */
224    function handle_cache(Doku_Event $event, $params) {
225        global $conf;
226        /** @var cache_parser $cache */
227        $cache = $event->data;
228        if (!in_array($cache->mode, array('xhtml', 'metadata'))) return;
229        $page = $cache->page;
230
231        // try to extract the page id from the file if possible
232        if (empty($page)) {
233            if (strpos($cache->file, $conf['datadir']) === 0) {
234                $page = pathID(substr($cache->file, strlen($conf['datadir'])+1));
235            } else {
236                return;
237            }
238        }
239
240        $meta = p_get_metadata($page, 'plugin_blog');
241        if ($meta === null) return;
242
243        if (isset($meta['purgefile_cache'])) {
244            $cache->depends['files'][] = $conf['cachedir'].'/purgefile';
245            $cache->depends['files'][] = $conf['metadir'].'/_comments.changes';
246            $cache->depends['files'][] = $conf['metadir'].'/_linkbacks.changes';
247        }
248
249        // purge the cache when a page is listed that the current user can't access
250        if (isset($meta['archive_pages'])) {
251            foreach ($meta['archive_pages'] as $page) {
252                if (auth_quickaclcheck($page) < AUTH_READ) {
253                    $cache->depends['purge'] = true;
254                    return;
255                }
256            }
257        }
258    }
259}
260