1<?php
2
3/**
4 * Display component of the DokuWiki Linkback action plugin. Highly influenced by
5 * the discussion plugin of Esther Brunner.
6 *
7 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author     Gina Haeussge <osd@foosel.net>
9 * @link       http://wiki.foosel.net/snippets/dokuwiki/linkback
10 */
11
12// must be run within Dokuwiki
13if (!defined('DOKU_INC'))
14    die();
15
16if (!defined('DOKU_PLUGIN'))
17    define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
18require_once (DOKU_PLUGIN . 'action.php');
19
20require_once (DOKU_INC . 'inc/common.php');
21require_once (DOKU_INC . 'inc/events.php');
22require_once (DOKU_PLUGIN . 'linkback/tools.php');
23
24if (!defined('NL'))
25    define('NL', "\n");
26
27class action_plugin_linkback_display extends DokuWiki_Action_Plugin {
28
29    /**
30     * A little helper.
31     */
32    var $tools;
33
34    /**
35     * Constructor
36     */
37    function __construct() {
38        $this->tools =& plugin_load('tools', 'linkback');
39    }
40
41    /**
42     * Register the eventhandlers.
43     */
44    function register(Doku_Event_Handler $controller) {
45        $controller->register_hook('TPL_ACT_RENDER', 'AFTER', $this, 'handle_act_render', array ());
46        $controller->register_hook('RENDERER_CONTENT_POSTPROCESS', 'AFTER', $this, 'handle_content_postprocess', array ());
47    }
48
49    /**
50     * Handler for the TPL_ACT_RENDER event
51     */
52    function handle_act_render(Doku_Event $event, $params) {
53        global $ID, $INFO;
54
55        if ($event->data != 'show')
56            return;
57
58        $file = metaFN($ID, '.linkbacks');
59        $data = array (
60            'send' => false,
61            'receive' => false,
62            'display' => false,
63            'sentpings' => array (),
64            'receivedpings' => array (),
65            'number' => 0,
66
67        );
68
69        if (@ file_exists($file))
70            $data = unserialize(io_readFile($file, false));
71
72        if (!isset ($_REQUEST['linkback']) || $INFO['perm'] != AUTH_ADMIN)
73            $_REQUEST['linkback'] = 'linkback_show';
74
75        $lid = $_REQUEST['lid'];
76        if (!preg_match('![a-zA-Z0-9]!', $lid))
77            $_REQUEST['linkback'] = 'linkback_show';
78
79        switch ($_REQUEST['linkback']) {
80            // toggle show/hide
81            case 'linkback_toggle' :
82                $linkback = $data['receivedpings'][$lid];
83                $data = $this->_changeLinkbackVisibilities(array($lid), !$linkback['show']);
84                break;
85            // delete linkback
86            case 'linkback_delete' :
87            	$data = $this->_deleteLinkbacks(array($lid));
88                break;
89                // report linkback as ham
90            case 'linkback_ham' :
91            	$this->_markLinkbacks(array($lid), false);
92                break;
93                // report linkback as spam
94            case 'linkback_spam' :
95            	$this->_markLinkbacks(array($lid), true);
96                break;
97        }
98
99        $this->_show($data);
100    }
101
102    /**
103     * Shows all linkbacks for the current page
104     */
105    function _show($data) {
106        global $ID;
107
108        if (!$data['display'])
109            return;
110
111        if ((count($data['receivedpings']) == 0) &&
112                (!$this->getConf('show_trackback_url') || !$this->getConf('enable_trackback')))
113            return;
114
115        // section title
116        $title = $this->getLang('linkbacks');
117        echo '<div class="linkback_wrapper">';
118        echo '<h2><a name="linkback__section" id="linkback__section">' . $title . '</a></h2>';
119        if ($this->getConf('show_trackback_url') && $data['receive']) {
120            echo '<div class="level2 hfeed linkback_trackbackurl">';
121            echo $this->getLang('trackback_url');
122            echo '<span class="linkback_trackbackurl">' . DOKU_URL . 'lib/plugins/linkback/exe/trackback.php/' . $ID . '</span>';
123            echo '</div>';
124        }
125        echo '<div class="level2 hfeed">';
126
127        // now display the comments
128        if (isset ($data['receivedpings'])) {
129            foreach ($data['receivedpings'] as $key => $ping) {
130                $this->_print($key, $ping);
131            }
132        }
133
134        echo '</div>'; // level2
135        echo '</div>'; // comment_wrapper
136
137        return true;
138    }
139
140    /**
141     * Prints an individual linkback
142     */
143    function _print($lid, $linkback, $visible = true) {
144        global $ID;
145        global $INFO;
146        global $conf;
147
148        if (!is_array($linkback))
149            return false; // corrupt datatype
150
151        if (!$linkback['show']) { // linkback hidden
152            if ($INFO['perm'] == AUTH_ADMIN)
153                echo '<div class="linkback_hidden">' . NL;
154            else
155                return true;
156        }
157
158        $title = $linkback['title'];
159        $url = $linkback['url'];
160        $date = $linkback['received'];
161        $excerpt = $linkback['excerpt'];
162        $icon = $linkback['favicon'];
163        $type = $linkback['type'];
164
165        // linkback head with date and link
166        echo '<div class="hentry"><div class="linkback_head">' . NL .
167        '<a name="linkback__' . $lid . '" id="linkback__' . $lid . '"></a>' . NL .
168        '<span class="sender">';
169
170        // show favicon image
171        if ($this->getConf('usefavicon')) {
172            if (!$icon)
173                $icon = $this->getConf('favicon_default');
174
175            $size = 16;
176            $src = ml($icon);
177            echo '<img src="' . $src . '" class="medialeft photo" title="' . $url . '"' .
178            ' width="' . $size . '" height="' . $size . '" />' . NL;
179            $style = ' style="margin-left: ' . ($size +14) . 'px;"';
180        } else {
181            $style = ' style="margin-left: 20px;"';
182        }
183
184        echo $this->external_link($url, ($linkback['blog_title'] ? $linkback['blog_title'] . ': ' : '') . $title, 'urlextern url fn');
185        echo '</span>, <abbr class="received" title="' . gmdate('Y-m-d\TH:i:s\Z', $date) .
186        '">' . strftime($conf['dformat'], $date) . '</abbr>';
187        echo ' (<abbr class="type" title="' . $this->getLang('linkback_type_' . $type) . '">' . $this->getLang('linkback_type_' . $type) . '</abbr>)';
188        echo NL . '</div>' . NL; // class="linkback_head"
189
190        // main linkback content
191        if (strlen($excerpt) > 0) {
192            echo '<div class="linkback_body entry-content"' .
193             ($this->getConf('usefavicon') ? $style : '') . '>' . NL .
194            $excerpt . NL . '</div>' . NL; // class="linkback_body"
195        }
196        echo '</div>' . NL; // class="hentry"
197
198        echo '<div class="linkback_buttons">' . NL;
199        if ($INFO['perm'] == AUTH_ADMIN) {
200            if (!$linkback['show'])
201                $label = $this->getLang('btn_show');
202            else
203                $label = $this->getLang('btn_hide');
204
205            $this->_button($lid, $label, 'linkback_toggle');
206            $this->_button($lid, $this->getLang('btn_ham'), 'linkback_ham');
207            $this->_button($lid, $this->getLang('btn_spam'), 'linkback_spam');
208            $this->_button($lid, $this->getLang('btn_delete'), 'linkback_delete');
209        }
210        echo '</div>';
211
212        echo '<div class="linkback_line" ' . ($this->getConf('usefavicon') ? $style : '') . '>&nbsp;</div>' . NL;
213
214        if (!$linkback['show'])
215            echo '</div>' . NL; // class="linkback_hidden"
216    }
217
218    /**
219     * General button function.
220     *
221     * Code mostly taken from the discussion plugin by Esther Brunner.
222     */
223    function _button($lid, $label, $act) {
224        global $ID;
225?>
226    <form class="button" method="post" action="<?php echo script() ?>">
227      <div class="no">
228        <input type="hidden" name="id" value="<?php echo $ID ?>" />
229        <input type="hidden" name="do" value="show" />
230        <input type="hidden" name="linkback" value="<?php echo $act ?>" />
231        <input type="hidden" name="lid" value="<?php echo $lid ?>" />
232        <input type="submit" value="<?php echo $label ?>" class="button" title="<?php echo $label ?>" />
233      </div>
234    </form>
235    <?php
236
237
238        return true;
239    }
240
241    /**
242     * Adds a TOC entry for the linkback section.
243     *
244     * Code mostly taken from the discussion plugin by Esther Brunner.
245     */
246    function handle_content_postprocess(Doku_Event $event, $params) {
247        global $ID;
248        global $conf;
249
250        if ($event->data[0] != 'xhtml')
251            return; // nothing to do for us
252
253        $file = metaFN($ID, '.linkbacks');
254        if (!@ file_exists($file))
255            return false;
256        $data = unserialize(io_readFile($file, false));
257        if (!$data['display'] || (count($data['receivedpings']) == 0))
258            return; // no linkback section
259
260        $pattern = '/<div id="toc__inside">(.*?)<\/div>\s<\/div>/s';
261        if (!preg_match($pattern, $event->data[1], $match))
262            return; // no TOC on this page
263
264        $title = $this->getLang('linkbacks');
265        $section = '#linkback__section';
266        $level = 3 - $conf['toptoclevel'];
267
268        $item = '<li class="level' . $level . '"><div class="li"><span class="li"><a href="' .
269        $section . '" class="toc">' . $title . '</a></span></div></li>';
270
271        if ($level == 1)
272            $search = "</ul>\n</div>";
273        else
274            $search = "</ul>\n</li></ul>\n</div>";
275
276        $new = str_replace($search, $item . $search, $match[0]);
277        $event->data[1] = preg_replace($pattern, $new, $event->data[1]);
278    }
279
280    function _changeLinkbackVisibilities($lids, $visible) {
281        global $ID;
282
283        $file = metaFN($ID, '.linkbacks');
284        $data = array (
285            'send' => false,
286            'receive' => false,
287            'display' => false,
288            'sentpings' => array (),
289            'receivedpings' => array (),
290            'number' => 0,
291
292        );
293
294        if (@ file_exists($file))
295            $data = unserialize(io_readFile($file, false));
296        $update = false;
297
298        foreach ($lids as $lid) {
299            $linkback = $data['receivedpings'][$lid];
300            if ($linkback['show'] == $visible)
301                continue;
302
303            $linkback['show'] = $visible;
304            if ($linkback['show'])
305                $data['number']++;
306            else
307                $data['number']--;
308            $data['receivedpings'][$lid] = $linkback;
309            $this->tools->addLogEntry($linkback['received'], $ID, (($linkback['show']) ? 'sl' : 'hl'), '', $linkback['lid']);
310            $update = true;
311        }
312
313        if ($update)
314            io_saveFile($file, serialize($data));
315        return $data;
316    }
317
318    function _deleteLinkbacks($lids) {
319        global $ID;
320
321        $file = metaFN($ID, '.linkbacks');
322        $data = array (
323            'send' => false,
324            'receive' => false,
325            'display' => false,
326            'sentpings' => array (),
327            'receivedpings' => array (),
328            'number' => 0,
329
330        );
331
332        if (@ file_exists($file))
333            $data = unserialize(io_readFile($file, false));
334        $update = false;
335
336        foreach ($lids as $lid) {
337            $linkback = $data['receivedpings'][$lid];
338            unset ($data['receivedpings'][$lid]);
339            if ($linkback['show'])
340                $data['number']--;
341            $this->tools->addLogEntry($linkback['received'], $ID, 'dl', '', $linkback['lid']);
342            $update = true;
343        }
344
345        if ($update)
346            io_saveFile($file, serialize($data));
347        return $data;
348    }
349
350    function _markLinkbacks($lids, $isSpam) {
351        global $ID;
352
353        $file = metaFN($ID, '.linkbacks');
354        $data = array (
355            'send' => false,
356            'receive' => false,
357            'display' => false,
358            'sentpings' => array (),
359            'receivedpings' => array (),
360            'number' => 0,
361
362        );
363
364        if (@ file_exists($file))
365            $data = unserialize(io_readFile($file, false));
366
367        foreach ($lids as $lid) {
368            $linkback = $data['receivedpings'][$lid];
369            if ($isSpam)
370                trigger_event('ACTION_LINKBACK_SPAM', $linkback);
371            else
372                trigger_event('ACTION_LINKBACK_HAM', $linkback);
373        }
374    }
375
376}
377