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