1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Gina Haeussge <osd@foosel.net>
5 */
6
7/**
8 * Class action_plugin_blogtng_feed
9 */
10class action_plugin_blogtng_feed extends DokuWiki_Action_Plugin{
11
12    /** @var helper_plugin_blogtng_entry */
13    private $entryhelper = null;
14    /** @var helper_plugin_blogtng_tools */
15    private $tools = null;
16
17    private $defaultConf = array(
18        'sortby' => 'created',
19        'sortorder' => 'DESC',
20    );
21
22    public function __construct() {
23        $this->entryhelper = plugin_load('helper', 'blogtng_entry');
24        $this->tools = plugin_load('helper', 'blogtng_tools');
25    }
26
27    /**
28     * Registers a callback function for a given event
29     *
30     * @param Doku_Event_Handler $controller
31     */
32    public function register(Doku_Event_Handler $controller) {
33        $controller->register_hook('FEED_OPTS_POSTPROCESS', 'AFTER', $this, 'handleBlogFeedParameters', array());
34        $controller->register_hook('FEED_MODE_UNKNOWN', 'BEFORE', $this, 'handleBlogFeed', array ());
35        $controller->register_hook('FEED_ITEM_ADD', 'BEFORE', $this, 'handleBlogpostAddedToFeed', array());
36    }
37
38    /**
39     * Parses blogtng specific feed parameters if the feed mode is 'blogtng'.
40     *
41     * @param Doku_Event $event  event object by reference
42     * @param array      $param  empty array as passed to register_hook()
43     * @return void
44     */
45    public function handleBlogFeedParameters(Doku_Event $event, $param) {
46        $opt =& $event->data['opt'];
47        if ($opt['feed_mode'] != 'blogtng') return;
48
49        global $INPUT;
50
51        $opt['blog'] = $INPUT->str('blog');
52        $opt['tags'] = $INPUT->str('tags');
53        $opt['sortby'] = $INPUT->str('sortby');
54        $opt['sortorder'] = $INPUT->str('sortorder');
55    }
56
57    /**
58     * Handles the 'blogtng' feed mode and prevents the default action (recents).
59     * Retrieves all blog posts as defined by blog and tags parameters, orders
60     * and limits them as requested and returns them inside the event.
61     *
62     * @param Doku_Event   $event the event as triggered in feed.php
63     * @param array        $param empty
64     * @return void
65     */
66    public function handleBlogFeed(Doku_Event $event, $param) {
67        $opt = $event->data['opt'];
68        if ($opt['feed_mode'] != 'blogtng') return;
69
70        $event->preventDefault();
71        $event->data['data'] = [];
72        $conf = [
73            'blog' => explode(',', $opt['blog']),
74            'tags' => ($opt['tags'] ? explode(',', $opt['tags']) : null),
75            'sortby' => $opt['sortby'],
76            'sortorder' => $opt['sortorder'],
77            'limit' => $opt['items'],
78            'offset' => 0,
79        ];
80        $this->tools->cleanConf($conf);
81        $conf = array_merge($conf, $this->defaultConf);
82        $posts = $this->entryhelper->get_posts($conf);
83        foreach ($posts as $row) {
84            $event->data['data'][] = array(
85                'id' => $row['page'],
86                'date' => $row['created'],
87                'user' => $row['author'],
88                'entry' => $row,
89            );
90        }
91    }
92
93    /**
94     * Preprocesses a blog post as its added to the feed. Makes sure to
95     * remove the first header from the text (otherwise it would be doubled)
96     * and takes care of presentation as configured via template.
97     *
98     * @param Doku_Event $event the event as triggered in feed.php
99     * @param array      $param empty
100     * @return void
101     */
102    public function handleBlogpostAddedToFeed(Doku_Event $event, $param) {
103        $opt = $event->data['opt'];
104        $ditem = $event->data['ditem'];
105        if ($opt['feed_mode'] !== 'blogtng') return;
106        if ($opt['item_content'] !== 'html') return;
107        if ($opt['link_to'] !== 'current') return;
108
109        // don't add drafts to the feed
110        if(p_get_metadata($ditem['id'], 'type') == 'draft') {
111            $event->preventDefault();
112            return;
113        }
114
115        // retrieve first heading from page instructions
116        $ins = p_cached_instructions(wikiFN($ditem['id']));
117        $headers = array_filter($ins, array($this, 'filterHeaders'));
118        $headingIns = array_shift($headers);
119        $firstheading = $headingIns[1][0];
120
121        $this->entryhelper->load_by_row($ditem['entry']);
122
123        ob_start();
124        $this->entryhelper->tpl_content($ditem['entry']['blog'], 'feed');
125        $output = ob_get_contents();
126        ob_end_clean();
127        // make URLs work when canonical is not set, regexp instead of rerendering!
128        global $conf;
129        if(!$conf['canonical']){
130            $base = preg_quote(DOKU_REL,'/');
131            $output = preg_replace('/(<a href|<img src)="('.$base.')/s','$1="'.DOKU_URL,$output);
132        }
133
134        // strip first heading and replace item title
135        $event->data['item']->description = preg_replace('#[^\n]*?>\s*?' . preg_quote(hsc($firstheading), '#') . '\s*?<.*\n#', '', $output, 1);
136        $event->data['item']->title = $ditem['entry']['title'];
137
138        //only supported by RSS 0.91 and RSS 2.0
139        if($ditem['entry']['commentstatus'] !== 'disabled') {
140            $event->data['item']->comments = $event->data['item']->link.'#the__comments';
141        }
142    }
143
144    /**
145     * Returns true if $entry is a valid header instruction, false otherwise.
146     *
147     * @author Gina Häußge <osd@foosel.net>
148     *
149     * @param $entry
150     * @return bool
151     */
152    private function filterHeaders($entry) {
153        // normal headers
154        if (is_array($entry) && $entry[0] == 'header' && count($entry) == 3 && is_array($entry[1]) && count($entry[1]) == 3)
155            return true;
156
157        // no known header
158        return false;
159    }
160}
161