1<?php
2
3use dokuwiki\Extension\ActionPlugin;
4use dokuwiki\Extension\Event;
5use dokuwiki\Extension\EventHandler;
6use dokuwiki\Form\InputElement;
7use FYKOS\dokuwiki\Extension\PluginFKSHelper\Form\DateTimeInputElement;
8use FYKOS\dokuwiki\Extension\PluginNewsFeed\Model\ModelNews;
9use dokuwiki\Form\Form;
10use dokuwiki\Cache\Cache;
11
12require_once __DIR__ . '/../fkshelper/inc/Form/DateTimeInputElement.php';
13
14/**
15 * Class action_plugin_newsfeed
16 * @author Michal Červeňák <miso@fykos.cz>
17 */
18class action_plugin_newsfeed extends ActionPlugin {
19
20    private static array $categories = [
21        'fykos-blue',
22        'fykos-pink',
23        'fykos-line',
24        'fykos-purple',
25        'fykos-orange',
26        'fykos-green',
27    ];
28
29    private helper_plugin_newsfeed $helper;
30
31    public function __construct() {
32        $this->helper = $this->loadHelper('newsfeed');
33    }
34
35    public function register(EventHandler $controller): void {
36        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax');
37        $controller->register_hook('ACTION_ACT_PREPROCESS', 'AFTER', $this, 'handleMetaData');
38        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleActPreprocess');
39        $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handleActUnknown');
40    }
41
42    public function handleActUnknown(Event $event): void {
43        global $ACT;
44        global $INPUT;
45        if ($ACT !== helper_plugin_newsfeed::FORM_TARGET) {
46            return;
47        }
48        if (auth_quickaclcheck('start') < AUTH_EDIT) {
49            return;
50        }
51        $event->preventDefault();
52        $event->stopPropagation();
53        switch ($INPUT->param('news')['do']) {
54            case'edit':
55            case'create':
56                $this->handleEditForm();
57                return;
58            case 'preview':
59                $this->handleStreamPreview();
60                return;
61        }
62    }
63
64    public function handleActPreprocess(Event $event): void {
65        global $INPUT;
66        if ($event->data !== helper_plugin_newsfeed::FORM_TARGET) {
67            return;
68        }
69        if (auth_quickaclcheck('start') < AUTH_EDIT) {
70            return;
71        }
72        $event->preventDefault();
73        $event->stopPropagation();
74        switch ($INPUT->param('news')['do']) {
75            case 'create':
76            case 'edit':
77            default:
78                return;
79            case'save':
80                $this->handleNews();
81                return;
82            case'priority':
83                $this->handlePriority();
84                return;
85            case'delete':
86                $this->handleDelete();
87                return;
88            case'purge':
89                $this->handleCache();
90                return;
91        }
92    }
93
94    public function handleAjax(Event $event): void {
95        $payload = json_decode(file_get_contents('php://input'), true);
96        if ($payload['target'] !== 'feed') {
97            return;
98        }
99        header('Content-Type: application/json');
100        $event->stopPropagation();
101        $event->preventDefault();
102
103        echo json_encode($this->printStream((int)$payload['news']['offset'], (int)$payload['news']['length'], $payload['news']['stream'], $payload['page_id']));
104    }
105
106    public function handleMetaData(): void {
107        global $ID;
108        global $INPUT;
109        if (!$INPUT->str('news-id')) {
110            return;
111        }
112
113        $newsId = $INPUT->str('news-id');
114        $news = $this->helper->serviceNews->getById($newsId);
115
116        $this->helper->openGraphData->addMetaData('og', 'title', $news->title);
117        $this->helper->openGraphData->addMetaData('og', 'url', $news->getToken($ID));
118        $text = $news->renderText();
119        $this->helper->openGraphData->addMetaData('og', 'description', $text);
120        if ($news->hasImage()) {
121            $this->helper->openGraphData->addMetaData('og', 'image', ml($news->image, null, true, '&', true));
122        }
123    }
124
125    private function printStream(int $start, int $length, string $streamId, string $pageId): array {
126
127        $stream = $this->helper->serviceStream->findByName($streamId);
128
129        $news = $stream->getNews();
130        $htmlNews = [];
131        for ($i = $start; $i < min([$start + $length, (count($news))]); $i++) {
132            $e = $i % 2 ? 'even' : 'odd';
133            $htmlNews[] = $news[$i]->render($e, $streamId, $pageId);
134        }
135        return ['html' => ['news' => $htmlNews]];
136    }
137
138
139    private function handleNews(): void {
140        global $INPUT;
141        $file = ModelNews::getCacheFileById($INPUT->param('news')['id']);
142        $cache = new Cache($file, '');
143        $cache->removeCache();
144
145        $data = [];
146        foreach (helper_plugin_newsfeed::$fields as $field) {
147            if ($field === 'text') {
148                $data[$field] = cleanText($INPUT->str('text'));
149            } else {
150                $data[$field] = $INPUT->param($field);
151            }
152        }
153        $data = [
154            'title' => $data['title'],
155            'author_name' => $data['authorName'],
156            'author_email' => $data['authorEmail'],
157            'text' => $data['text'],
158            'news_date' => $data['newsDate'],
159            'image' => $data['image'] ?: null,
160            'category' => $data['category'],
161            'link_href' => $data['linkHref'] ?: null,
162            'link_title' => $data['linkTitle'] ?: null,
163        ];
164
165        if (+$INPUT->param('news')['id'] === 0) {
166            $this->helper->serviceNews->create($data);
167            $this->saveIntoStreams($this->helper->serviceNews->getMaxId(), $INPUT->param('news')['stream']);
168        } else {
169            $news = $this->helper->serviceNews->getById($INPUT->param('news')['id']);
170            $this->helper->serviceNews->update($news, $data);
171        }
172        header('Location: ' . $_SERVER['REQUEST_URI']);
173        exit();
174    }
175
176    private function saveIntoStreams(int $newsId, string $streamName): void {
177        $stream = $this->helper->serviceStream->findByName($streamName);
178
179        $streams = [$stream->streamId];
180        $this->helper->fullParentDependence($stream->streamId, $streams);
181
182        foreach ($streams as $stream) {
183            $this->helper->servicePriority->store($newsId, $stream);
184        }
185    }
186
187    private function handlePriority(): void {
188        global $INPUT;
189        $file = ModelNews::getCacheFileById($INPUT->param('news')['id']);
190
191        $cache = new cache($file, '');
192        $cache->removeCache();
193        $stream = $this->helper->serviceStream->findByName($INPUT->param('news')['stream']);
194
195        $priority = $this->helper->servicePriority->findByNewsAndStream($INPUT->param('news')['id'], $stream->streamId);
196        $data = $INPUT->param('priority');
197        $this->helper->servicePriority->update($priority, [
198            'priority_from' => $data['from'],
199            'priority_to' => $data['to'],
200            'priority' => $data['value'],
201        ]);
202        header('Location: ' . $_SERVER['REQUEST_URI']);
203        exit();
204    }
205
206    private function handleDelete(): void {
207        global $INPUT;
208        $stream = $this->helper->serviceStream->findByName($INPUT->param('news')['stream']);
209        $priority = $this->helper->servicePriority->findByNewsAndStream($INPUT->param('news')['id'], $stream->streamId);
210        $priority->delete();
211        header('Location: ' . $_SERVER['REQUEST_URI']);
212        exit();
213    }
214
215    private function handleCache(): void {
216        global $INPUT;
217        if (!$INPUT->param('news')['id']) {
218            $news = $this->helper->serviceNews->getAll();
219            foreach ($news as $new) {
220                $f = $new->getCacheFile();
221                $cache = new Cache($f, '');
222                $cache->removeCache();
223            }
224        } else {
225            $f = ModelNews::getCacheFileById($INPUT->param('news')['id']);
226            $cache = new Cache($f, '');
227            $cache->removeCache();
228        }
229        header('Location: ' . $_SERVER['REQUEST_URI']);
230        exit();
231    }
232
233    private function handleStreamPreview(): void {
234        global $INPUT;
235        if ($INPUT->param('news')['stream']) {
236            echo p_render('xhtml', p_get_instructions('{{news-stream>feed="5";stream="' . $INPUT->param('news')['stream'] . '"}}'), $info);
237        } else {
238            msg('Stream is required.', -1);
239        }
240    }
241
242    private function handleEditForm(): void {
243        global $INPUT;
244        global $ID;
245
246        $form = new Form();
247
248        if ($INPUT->param('news')['id'] !== 0) {
249            $data = $this->helper->serviceNews->getById($INPUT->param('news')['id']);
250        } else {
251            $data = new ModelNews($this->helper->sqlite);
252            $data->loadDefault();
253        }
254        $form->setHiddenField('page_id', $ID);
255        $form->setHiddenField('do', helper_plugin_newsfeed::FORM_TARGET);
256        $form->setHiddenField('news[id]', $INPUT->param('news')['id']);
257        $form->setHiddenField('news[do]', 'save');
258        $form->setHiddenField('news[stream]', $INPUT->param('news')['stream']);
259        $form->addFieldsetOpen('Novinky na webu');
260
261        foreach (helper_plugin_newsfeed::$fields as $field) {
262            $input = null;
263            $form->addTagOpen('div')->addClass('form-group');
264
265            switch ($field) {
266                case'text':
267                    $input = $form->addTextarea('text', $this->getLang($field), -1)->attrs([
268                        'class' => 'form-control',
269                        'rows' => 10,
270                    ]);
271                    $input->val($data->text);
272                    break;
273                case 'newsDate':
274                    $input = new DateTimeInputElement($field, $this->getLang($field));
275                    $input->attr('class', 'form-control');
276                    $input->setStep(1);
277                    $form->addElement($input);
278                    $input->val($data->newsDate ?: 'now');
279                    break;
280                case'category':
281                    $input = $form->addDropdown('category', static::$categories, $this->getLang($field))->attr('class', 'form-control');
282                    $input->val($data->category);
283                    break;
284                case'image':
285                    $input = $form->addTextInput($field, $this->getLang($field))->attr('class', 'form-control');
286                    $input->val($data->image);
287                    break;
288                case 'linkHref':
289                    $input = $form->addTextInput($field, $this->getLang($field))->attrs([
290                        'class' => 'form-control',
291                    ]);
292                    $input->val($data->linkHref);
293                    break;
294                case 'linkTitle':
295                    $input = $form->addTextInput($field, $this->getLang($field))->attrs([
296                        'class' => 'form-control',
297                    ]);
298                    $input->val($data->linkTitle);
299                    break;
300                case 'authorName':
301                    $input = $form->addTextInput($field, $this->getLang($field))->attrs([
302                        'pattern' => '\S.*',
303                        'required' => 'required',
304                        'class' => 'form-control',
305                    ]);
306                    $input->val($data->authorName);
307                    break;
308                case 'authorEmail':
309                    $input = new InputElement('email', $field, $this->getLang($field));
310                    $input->attr('class', 'form-control');
311                    $form->addElement($input);
312                    $input->val($data->authorEmail);
313                    break;
314                case 'title':
315                    $input = $form->addTextInput($field, $this->getLang($field))->attrs([
316                        'pattern' => '\S.*',
317                        'required' => 'required',
318                        'class' => 'form-control',
319                    ]);
320                    $input->val($data->title);
321                    break;
322                default:
323                    msg('Not implement input field ' . $field, -1);
324            }
325            if ($note = $this->getLang($field . '_note')) {
326                $form->addHTML('<small>' . $note . '</small>');
327            }
328            $form->addTagClose('div');
329        }
330        $form->addFieldsetClose();
331        $form->addButton('submit', $this->getLang('save'))->addClass('btn btn-success');
332        echo $form->toHTML();
333    }
334}
335