1<?php
2/**
3 * Eventum Plugin:  Evetnum SCM addons
4 *
5 * interpret eventum issue tags in DokuWiki
6 *
7 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author     Elan Ruusamäe <glen@delfi.ee>
9 */
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13if (file_exists($autoload = __DIR__. '/vendor/autoload.php')) {
14    require_once $autoload;
15} else {
16    // try from include path
17    require_once 'XML/RPC.php';
18    require_once 'class.Eventum_RPC.php';
19}
20
21/**
22 * All DokuWiki plugins to extend the parser/rendering mechanism
23 * need to inherit from this class
24 */
25class syntax_plugin_eventum extends DokuWiki_Syntax_Plugin {
26    /*
27     * keys that we use for caching, to avoid running out of memory when serializing
28     */
29    static $cache_keys = array(
30        'iss_summary' => 'summary',
31        'sta_title' => 'status',
32        'sta_is_closed' => 'is_closed',
33    );
34
35    /**
36     * What kind of syntax are we?
37     */
38    function getType() {
39        return 'substition';
40    }
41
42    /**
43     * Where to sort in?
44     */
45    function getSort() {
46        return 290;
47    }
48
49    /**
50     * Connect pattern to lexer
51     */
52    function connectTo($mode) {
53        $this->Lexer->addSpecialPattern('\[\[issue>.+?\]\]', $mode, 'plugin_eventum');
54    }
55
56
57    /**
58     * Handle the match
59     */
60    function handle($match, $state, $pos, Doku_Handler $handler) {
61        $raw = $match = substr($match, 8, -2);
62        // extract title
63        list($match, $title) = explode('|', $match, 2);
64        // extract id
65        list($id, $attrs) = explode('&', $match, 2);
66
67        $data = array('raw' => $raw, 'id' => $id, 'attrs' => $attrs);
68
69        // set title only when specified to be able to make difference between empty and unset
70        if ($title !== null) {
71            $data['title'] = trim($title);
72        }
73
74        return $data;
75    }
76
77    function cache($id, $data = null) {
78        global $conf;
79        $cachefile = $conf['cachedir'].'/'.$this->getPluginName().'.cache';
80
81        // mode: get but no cachefile
82        if ($data === null && !file_exists($cachefile)) {
83            return null;
84        }
85
86        // read cache
87        $cache = array();
88        if (file_exists($cachefile)) {
89            $cache = unserialize(file_get_contents($cachefile));
90        }
91
92        // expire as long as page edit time to make page edit smooth but still
93        // have almost accurate data.
94        $mtime = time() - $conf['locktime'];
95        foreach ($cache as $i => $ent) {
96            if ($ent['mtime'] < $mtime) {
97                unset($cache[$i]);
98            }
99        }
100
101        // mode get:
102        if ($data === null) {
103            return isset($cache[$id]) ? $cache[$id] : null;
104        }
105
106        // mode: set
107        $cache[$id] = $data;
108        file_put_contents($cachefile, serialize($cache));
109        return true;
110    }
111
112    /*
113     * combine keys from $data array to new one.
114     * rename keys to be named as $value says.
115     */
116    function filter_keys($keys, $data) {
117        $res = array();
118        foreach ($keys as $key => $value) {
119            // remap old key to new one
120            if (isset($data[$key])) {
121                $res[$value] = $data[$key];
122                continue;
123            }
124            // use already existing new name
125            if (isset($data[$value])) {
126                $res[$value] = $data[$value];
127                continue;
128            }
129        }
130        return $res;
131    }
132
133    /**
134     * Query data from Eventum server
135     */
136    function query($id) {
137        global $conf;
138
139        $cache = $this->cache($id);
140        if ($cache !== null) {
141            return $cache;
142        }
143
144        static $client = null;
145        static $eventum_url, $rpc_url;
146
147        if (!$client) {
148            // setup RPC object
149            $rpc_url = $this->getConf('url') . '/rpc/xmlrpc.php';
150            $client = new Eventum_RPC($rpc_url);
151            $client->setCredentials($this->getConf('username'), $this->getConf('password'));
152            //$client->setDebug(1);
153
154            // Issue link
155            $eventum_url = $this->getConf('url') . '/view.php?id=';
156        }
157        $data['url'] = $eventum_url . $id;
158
159        try {
160            $data['details'] = self::filter_keys(self::$cache_keys, $client->getSimpleIssueDetails((int )$id));
161
162        } catch (Eventum_RPC_Exception $e) {
163            $data['error'] = $e->getMessage();
164
165            if ($conf['allowdebug']) {
166                $data['rpcurl'] = $rpc_url;
167            }
168        }
169
170        $data['id'] = $id;
171        $data['mtime'] = time();
172        $this->cache($id, $data);
173
174        return $data;
175    }
176
177    /**
178     * Create output
179     */
180    function render($format, Doku_Renderer $renderer, $data) {
181        global $ID;
182
183        // fetch extra data from eventum
184        $data += $this->query($data['id']);
185
186        // link title
187        $link = 'issue #'. $data['id'];
188
189        if ($data['error']) {
190            if ($format == 'xhtml') {
191                $renderer->doc .= $link;
192                $renderer->doc .= ': <i style="color:red">'.$data['error'].'</i>';
193                if (isset($data['rpcurl'])) {
194                    $renderer->doc .= " <tt>RPC URL: {$data['rpcurl']}</tt>";
195                }
196            } else {
197                $renderer->cdata($data['error']);
198            }
199            return;
200        }
201
202        if (!isset($data['title'])) {
203            $data['title'] = $data['details']['summary'];
204        }
205
206        if ($format == 'xhtml' || $format == 'odt') {
207            $html = '';
208            $html .= $this->link($format, $data['url'], $link, $data['details']['summary']);
209            if ($data['title']) {
210                $html .= ': '. hsc($data['title']);
211            }
212
213            if ($data['details']['status']) {
214                $html .= ' '. $this->emphasis($format, '('.$data['details']['status'].')');
215            }
216
217            if ($data['details']['is_closed']) {
218                $html = $this->strike($format, $html);
219            }
220
221            $renderer->doc .= $this->html($format, $html);
222
223        } elseif ($format == 'odt') {
224            $renderer->externallink($data['url'], $link);
225            $renderer->cdata(': '.$data['title']);
226        }
227
228        return true;
229    }
230
231    /** odt/html export helpers, partly ripped from odt plugin */
232    function _xmlEntities($value) {
233        return str_replace( array('&','"',"'",'<','>'), array('&#38;','&#34;','&#39;','&#60;','&#62;'), $value);
234    }
235
236    function strike($format, $text) {
237        $doc = '';
238        if ($format == 'xhtml') {
239            $doc .= '<strike>';
240            $doc .= $text;
241            $doc .= '</strike>';
242        } elseif ($format == 'odt') {
243            $doc .= '<text:span text:style-name="del">';
244            $doc .= $text;
245            $doc .= '</text:span>';
246        }
247        return $doc;
248    }
249
250    function emphasis($format, $text) {
251        if ($format == 'xhtml') {
252            $doc .= '<i>';
253            $doc .= $text;
254            $doc .= '</i>';
255        } elseif ($format == 'odt') {
256            $doc .= '<text:span text:style-name="Emphasis">';
257            $doc .= $text;
258            $doc .= '</text:span>';
259        }
260        return $doc;
261    }
262
263    function html($format, $text) {
264        $doc = '';
265        if ($format == 'xhtml') {
266            $doc .= $text;
267        } elseif ($format == 'odt') {
268            $doc .= '<text:span>';
269            $doc .= $text;
270            $doc .= '</text:span>';
271        }
272        return $doc;
273    }
274
275    function link($format, $url, $name, $title) {
276        $doc = '';
277        if ($format == 'xhtml') {
278            $doc .= '<a class="iw_eventum" href="'.$url.'" target="_blank" title="'.$title.'">'.hsc($name).'</a>';
279
280        } elseif ($format == 'odt') {
281            $url = $this->_xmlEntities($url);
282            $doc .= '<text:a xlink:type="simple" xlink:href="'.$url.'">';
283            $doc .= $name; // we get the name already XML encoded
284            $doc .= '</text:a>';
285        }
286        return $doc;
287    }
288}
289
290//Setup VIM: ex: et ts=4 enc=utf-8 :
291