1<?php
2/**
3 * Archive Plugin: displays links to all wiki pages from a given month
4 *
5 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author   Esther Brunner <wikidesign@gmail.com>
7 */
8
9/**
10 * All DokuWiki plugins to extend the parser/rendering mechanism
11 * need to inherit from this class
12 */
13class syntax_plugin_blog_archive extends DokuWiki_Syntax_Plugin {
14
15    function getType() { return 'substition'; }
16    function getPType() { return 'block'; }
17    function getSort() { return 309; }
18
19    function connectTo($mode) {
20        $this->Lexer->addSpecialPattern('\{\{archive>.*?\}\}', $mode, 'plugin_blog_archive');
21    }
22
23    function handle($match, $state, $pos, Doku_Handler $handler) {
24        global $ID;
25
26        $match = substr($match, 10, -2); // strip {{archive> from start and }} from end
27        list($match, $flags) = explode('&', $match, 2);
28        $flags = explode('&', $flags);
29        list($match, $refine) = explode(' ', $match, 2);
30        list($ns, $rest) = explode('?', $match, 2);
31
32        $author = NULL;
33        foreach($flags as $i=>$flag) {
34            if(preg_match('/(\w+)\s*=(.+)/', $flag, $temp) == 1) {
35                if ($temp[1] == 'author') {
36                    $author = trim($temp[2]);
37                    unset($flags[$i]);
38                }
39            }
40        }
41
42        if (!$rest) {
43            $rest = $ns;
44            $ns   = '';
45        }
46
47        if ($ns == '') $ns = cleanID($this->getConf('namespace'));
48        elseif (($ns == '*') || ($ns == ':')) $ns = '';
49        elseif ($ns == '.') $ns = getNS($ID);
50        else $ns = cleanID($ns);
51
52        // daily archive
53        if (preg_match("/\d{4}-\d{2}-\d{2}/", $rest)) {
54            list($year, $month, $day) = explode('-', $rest, 3);
55
56            $start  = mktime(0, 0, 0, $month, $day, $year);
57            $end    = $start + 24*60*60;
58
59            // monthly archive
60        } elseif (preg_match("/\d{4}-\d{2}/", $rest)) {
61            list($year, $month) = explode('-', $rest, 2);
62
63            // calculate start and end times
64            $nextmonth   = $month + 1;
65            $year2       = $year;
66            if ($nextmonth > 12) {
67                $nextmonth = 1;
68                $year2     = $year + 1;
69            }
70
71            $start  = mktime(0, 0, 0, $month, 1, $year);
72            $end    = mktime(0, 0, 0, $nextmonth, 1, $year2);
73
74            // a whole year
75        } elseif (preg_match("/\d{4}/", $rest)) {
76            $start  = mktime(0, 0, 0, 1, 1, $rest);
77            $end    = mktime(0, 0, 0, 1, 1, $rest + 1);
78
79            // all entries from that namespace up to now
80        } elseif ($rest == '*') {
81            $start  = 0;
82            $end    = PHP_INT_MAX;
83
84            // unknown format
85        } else {
86            return false;
87        }
88
89        return array($ns, $start, $end, $flags, $refine, $author);
90    }
91
92    function render($mode, Doku_Renderer $renderer, $data) {
93        list($ns, $start, $end, $flags, $refine, $author) = $data;
94
95        // get the blog entries for our namespace
96        /** @var helper_plugin_blog $my */
97        if ($my = plugin_load('helper', 'blog')) $entries = $my->getBlog($ns, NULL, $author);
98        else return false;
99
100        // use tag refinements?
101        if ($refine) {
102            /** @var helper_plugin_tag $tag */
103            if (plugin_isdisabled('tag') || (!$tag = plugin_load('helper', 'tag'))) {
104                msg($this->getLang('missing_tagplugin'), -1);
105            } else {
106                $entries = $tag->tagRefine($entries, $refine);
107            }
108        }
109
110        if (!$entries) return true; // nothing to display
111
112        if ($mode == 'xhtml') {
113            if ($this->getConf('showhistogram')) {
114                $alt_list = $this->_build_alternative_list($start, $end, $entries);
115
116                // Add histogram and posts list
117                $renderer->doc .= '<div class="level1">';
118                $renderer->doc .= '<h1>' . $this->getLang('archive_title') . '</h1>';
119                $renderer->doc .= $alt_list;
120                $renderer->doc .= '</div>' . DOKU_LF;
121            } else {
122                // let Pagelist Plugin do the work for us
123                if (plugin_isdisabled('pagelist')
124                        || (!$pagelist = plugin_load('helper', 'pagelist'))) {
125                    msg($this->getLang('missing_pagelistplugin'), -1);
126                    return false;
127                }
128                /** @var helper_plugin_pagelist $pagelist */
129                $pagelist->setFlags($flags);
130                $pagelist->startList();
131                foreach ($entries as $entry) {
132
133                    // entry in the right date range?
134                    if (($start > $entry['date']) || ($entry['date'] >= $end)) continue;
135
136                    $pagelist->addPage($entry);
137                }
138                $renderer->doc .= $pagelist->finishList();
139            }
140            return true;
141
142            // for metadata renderer
143        } elseif ($mode == 'metadata') {
144            /** @var Doku_Renderer_metadata $renderer */
145            // use the blog plugin cache handler in order to ensure that the cache is expired whenever a page, comment
146            // or linkback is added
147            if (time() < $end) $renderer->meta['plugin_blog']['purgefile_cache'] = true;
148
149            foreach ($entries as $entry) {
150
151                // entry in the right date range?
152                if (($start > $entry['date']) || ($entry['date'] >= $end)) continue;
153
154                $renderer->meta['relation']['references'][$entry['id']] = true;
155                $renderer->meta['plugin_blog']['archive_pages'][] = $entry['id'];
156            }
157
158            return true;
159        }
160        return false;
161    }
162
163    // Generate alternative posts list
164    function _build_alternative_list($start, $end, $entries) {
165        $current_year ='';
166        $current_month ='';
167        $ul_open = false;
168
169        $histogram_count = array();
170        $histogram_higher = 0;
171
172        $list = '';
173        foreach ($entries as $entry) {
174            // entry in the right date range?
175            if (($start > $entry['date']) || ($entry['date'] >= $end)) continue;
176
177            if ($current_year != date('o',$entry['date'])) {
178                if ($ul_open) {
179                    $list .= '</ul>' . DOKU_LF;
180                    $ul_open = false;
181                }
182                $current_year = date('o',$entry['date']);
183                $list .= '<h2>' . $current_year . '</h2>' . DOKU_LF;
184                $current_month = '';
185            }
186            if ($current_month != date('m',$entry['date'])) {
187                if ($ul_open) {
188                    $list .= '</ul>' . DOKU_LF;
189                }
190                $current_month = date('m',$entry['date']);
191                $list .= '<h3 id="m' . date('o-m',$entry['date']) . '">' . $this->getLang('month_' . $current_month) . '</h3><ul>' . DOKU_LF;
192                $ul_open = true;
193            }
194            $histogram_count[date('o-m',$entry['date'])] += 1;
195            if ($histogram_higher < $histogram_count[date('o-m',$entry['date'])]) {
196                $histogram_higher = $histogram_count[date('o-m',$entry['date'])];
197            }
198            $list .= '<li>' . date('d',$entry['date']) . ' - <a href="' . wl($entry['id']) . '" title="' . $entry['id'] . '">' . $entry['title'] . '</a></li>' . DOKU_LF;
199        }
200        $list .= '</ul>' . DOKU_LF;
201
202        $histogram = $this->_build_histogram($histogram_count, $histogram_higher);
203
204        return $histogram . $list;
205    }
206
207    // Generate histogram
208    function _build_histogram($histogram_count, $histogram_higher) {
209        if (empty($histogram_count)) return '';
210
211        $histogram = '<p>';
212        $max_months = $this->getConf('max_months');
213        $histogram_height = $this->getConf('histogram_height');
214        $histogram_count = array_reverse($histogram_count);
215        $month_count = 0;
216        foreach ($histogram_count as $key => $month_reference) {
217            // Check the max_months parameter
218            if ($month_count >= $max_months) {
219                break;
220            }
221            if ($month_reference > 0) {
222                // Height in "px"
223                $current_height = $histogram_height / $histogram_higher * $month_reference;
224            } else {
225                // Height in "px"
226                $current_height = 1;
227            }
228            // Generate the alt attribute
229            $alt = $key.': '.$month_reference.' ';
230            if ($month_reference > 1) {
231                $alt .= $this->getLang('entries');
232            } else {
233                $alt .= $this->getLang('entry');
234            }
235            $histogram .= '<a href="#m' . $key . '" title="' . $alt . '">';
236            $histogram .= '<img class="blog_archive_bar" alt="' . $alt . '" style="height: ' . $current_height . 'px;" src="'.DOKU_BASE.'lib/images/blank.gif"/></a>' . DOKU_LF;
237            $month_count += 1;
238        }
239        $histogram .= '</p>';
240
241        return $histogram;
242    }
243
244}
245// vim:ts=4:sw=4:et:enc=utf-8:
246