1<?php
2
3require_once(__DIR__ . '/../vendor/autoload.php');
4
5use ComboStrap\ExceptionBadArgument;
6use ComboStrap\ExceptionBadSyntax;
7use ComboStrap\ExceptionNotEnabled;
8use ComboStrap\ExecutionContext;
9use ComboStrap\FetcherPage;
10use ComboStrap\FileSystems;
11use ComboStrap\Identity;
12use ComboStrap\LogUtility;
13use ComboStrap\LowQualityPage;
14use ComboStrap\MarkupPath;
15use ComboStrap\PageProtection;
16use ComboStrap\PagePublicationDate;
17use ComboStrap\Robots;
18use ComboStrap\Web\Url;
19use ComboStrap\WikiPath;
20
21
22/**
23 *
24 */
25class action_plugin_combo_pageprotection extends DokuWiki_Action_Plugin
26{
27
28
29    public function register(Doku_Event_Handler $controller)
30    {
31
32
33        /**
34         * https://www.dokuwiki.org/devel:event:pageutils_id_hidepage
35         */
36        $controller->register_hook('PAGEUTILS_ID_HIDEPAGE', 'BEFORE', $this, 'handleHiddenCheck', array());
37
38        /**
39         * https://www.dokuwiki.org/devel:event:auth_acl_check
40         */
41        $controller->register_hook('AUTH_ACL_CHECK', 'AFTER', $this, 'handleAclCheck', array());
42
43        /**
44         * https://www.dokuwiki.org/devel:event:sitemap_generate
45         */
46        $controller->register_hook('SITEMAP_GENERATE', 'BEFORE', $this, 'handleSiteMapGenerate', array());
47
48        /**
49         * https://www.dokuwiki.org/devel:event:search_query_pagelookup
50         */
51        $controller->register_hook('SEARCH_QUERY_PAGELOOKUP', 'AFTER', $this, 'handleSearchPageLookup', array());
52
53        /**
54         * https://www.dokuwiki.org/devel:event:search_query_fullpage
55         */
56        $controller->register_hook('SEARCH_QUERY_FULLPAGE', 'AFTER', $this, 'handleSearchFullPage', array());
57
58        /**
59         * https://www.dokuwiki.org/devel:event:feed_data_process
60         */
61        $controller->register_hook('FEED_DATA_PROCESS', 'BEFORE', $this, 'handleRssFeed', array());
62
63
64        /**
65         * Robots meta
66         * https://www.dokuwiki.org/devel:event:tpl_metaheader_output
67         */
68        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'handleRobotsMeta', array());
69
70
71    }
72
73    /**
74     * Set page has hidden
75     * @param $event
76     * @param $param
77     */
78    function handleHiddenCheck(&$event, $param)
79    {
80
81        /**
82         * Only for public
83         */
84        if (Identity::isLoggedIn()) {
85            return;
86        }
87
88        $id = $event->data['id'];
89        if ($id == null) {
90            /**
91             * Happens in test when rendering
92             * with instructions only
93             */
94            return;
95        }
96        $page = MarkupPath::createMarkupFromId($id);
97
98        if ($page->isLowQualityPage()) {
99            if (LowQualityPage::getLowQualityProtectionMode() == PageProtection::CONF_VALUE_HIDDEN) {
100                $event->data['hidden'] = true;
101                return;
102            }
103        }
104        if ($page->isLatePublication()) {
105            if (PagePublicationDate::getLatePublicationProtectionMode() == PageProtection::CONF_VALUE_HIDDEN) {
106                $event->data['hidden'] = true;
107            }
108
109        }
110
111    }
112
113    /**
114     *
115     * https://www.dokuwiki.org/devel:event:auth_acl_check
116     * @param $event
117     * @param $param
118     */
119    function handleAclCheck(&$event, $param)
120    {
121        /**
122         * Only for public
123         *
124         * Note: user is also
125         * to be found at
126         * $user = $event->data['user'];
127         */
128        if (Identity::isLoggedIn()) {
129            return;
130        }
131
132        /**
133         * Are we on a page script
134         */
135        $imageScript = ["/lib/exe/mediamanager.php", "/lib/exe/detail.php"];
136        if (in_array($_SERVER['SCRIPT_NAME'], $imageScript)) {
137            // id may be null or end with a star
138            // this is not a image
139            return;
140        }
141
142        $id = $event->data['id'];
143        if ($id == null) {
144            /**
145             * Happens in test when rendering
146             * with instructions only
147             */
148            return;
149        }
150
151        $dokuPath = WikiPath::createFromUnknownRoot($id);
152        if ($dokuPath->isPage()) {
153
154            /**
155             * It should be only a page
156             * https://www.dokuwiki.org/devel:event:auth_acl_check
157             */
158            $page = MarkupPath::createMarkupFromId($id);
159
160            if ($page->isLowQualityPage()) {
161                if ($this->getConf(LowQualityPage::CONF_LOW_QUALITY_PAGE_PROTECTION_ENABLE, true)) {
162                    $securityConf = $this->getConf(LowQualityPage::CONF_LOW_QUALITY_PAGE_PROTECTION_MODE, PageProtection::CONF_VALUE_ACL);
163                    if ($securityConf == PageProtection::CONF_VALUE_ACL) {
164                        $event->result = AUTH_NONE;
165                        return;
166                    }
167                }
168            }
169            if ($page->isLatePublication()) {
170                if ($this->getConf(PagePublicationDate::CONF_LATE_PUBLICATION_PROTECTION_ENABLE, true)) {
171                    $securityConf = $this->getConf(PagePublicationDate::CONF_LATE_PUBLICATION_PROTECTION_MODE, PageProtection::CONF_VALUE_ACL);
172                    if ($securityConf == PageProtection::CONF_VALUE_ACL) {
173                        $event->result = AUTH_NONE;
174                        return;
175                    }
176                }
177            }
178
179        }
180
181    }
182
183    function handleSiteMapGenerate(&$event, $param)
184    {
185        $pageItems = $event->data["items"];
186        foreach ($pageItems as $key => $pageItem) {
187
188            try {
189                $url = Url::createFromString($pageItem->url);
190                $fetcherPage = FetcherPage::createPageFragmentFetcherFromUrl($url);
191            } catch (ExceptionBadArgument|ExceptionBadSyntax  $e) {
192                LogUtility::internalError("We were unable to build the page fetcher. Error: " . $e->getMessage(), "sitemap", $e);
193                continue;
194            }
195
196            $page = MarkupPath::createPageFromPathObject($fetcherPage->getSourcePath());
197            if ($page->isLowQualityPage() && LowQualityPage::isProtectionEnabled()) {
198
199                unset($event->data["items"][$key]);
200                continue;
201
202            }
203            if ($page->isLatePublication() && PagePublicationDate::isLatePublicationProtectionEnabled()) {
204                unset($event->data["items"][$key]);
205            }
206            /**
207             * Url rewrite
208             */
209            $urlAfterRewrite = $page->getCanonicalUrl()->toString();
210            $event->data["items"][$key]->url = $urlAfterRewrite;
211        }
212
213    }
214
215    /**
216     * @param $event
217     * @param $param
218     * The autocomplete do a search on page name
219     */
220    function handleSearchPageLookup(&$event, $param)
221    {
222        $this->excludePageFromSearch($event);
223    }
224
225    /**
226     * @param $event
227     * @param $param
228     * The search page do a search on page name
229     */
230    function handleSearchFullPage(&$event, $param)
231    {
232
233        $this->excludePageFromSearch($event);
234    }
235
236    /**
237     *
238     * @param $event
239     * @param $param
240     * The Rss
241     * https://www.dokuwiki.org/syndication
242     * Example
243     * https://example.com/feed.php?type=rss2&num=5
244     */
245    function handleRssFeed(&$event, $param)
246    {
247        $isLowQualityProtectionEnabled = LowQualityPage::isProtectionEnabled();
248        $isLatePublicationProtectionEnabled = PagePublicationDate::isLatePublicationProtectionEnabled();
249        if (!$isLatePublicationProtectionEnabled && !$isLowQualityProtectionEnabled) {
250            return;
251        }
252
253        $pagesToBeAdded = &$event->data["data"];
254        foreach ($pagesToBeAdded as $key => $data) {
255
256            // To prevent an Illegal string offset 'id'
257            if (isset($data["id"])) {
258
259                $page = MarkupPath::createMarkupFromId($data["id"]);
260
261                if ($page->isLowQualityPage() && $isLowQualityProtectionEnabled) {
262                    $protectionMode = LowQualityPage::getLowQualityProtectionMode();
263                    if ($protectionMode != PageProtection::CONF_VALUE_ROBOT) {
264                        unset($pagesToBeAdded[$key]);
265                    }
266                }
267
268                if ($page->isLatePublication() && $isLatePublicationProtectionEnabled) {
269                    $protectionMode = PagePublicationDate::getLatePublicationProtectionMode();
270                    if ($protectionMode != PageProtection::CONF_VALUE_ROBOT) {
271                        unset($pagesToBeAdded[$key]);
272                    }
273                }
274
275            }
276        }
277
278    }
279
280    /**
281     * @param $event
282     * @param array $protectionModes
283     */
284    private
285    function excludePageFromSearch(&$event, $protectionModes = [PageProtection::CONF_VALUE_ACL, PageProtection::CONF_VALUE_HIDDEN])
286    {
287
288        /**
289         * The value is always an array
290         * but as we got this error:
291         * ```
292         * array_keys() expects parameter 1 to be array
293         * ```
294         * The result is a list of page id
295         */
296        if (is_array($event->result)) {
297            foreach (array_keys($event->result) as $idx) {
298                $page = MarkupPath::createMarkupFromId($idx);
299                if ($page->isLowQualityPage()) {
300                    $securityConf = $this->getConf(LowQualityPage::CONF_LOW_QUALITY_PAGE_PROTECTION_MODE);
301                    if (in_array($securityConf, $protectionModes)) {
302                        unset($event->result[$idx]);
303                        return;
304                    }
305                }
306                if ($page->isLatePublication()) {
307                    $securityConf = $this->getConf(PagePublicationDate::CONF_LATE_PUBLICATION_PROTECTION_MODE);
308                    if (in_array($securityConf, [PageProtection::CONF_VALUE_ACL, PageProtection::CONF_VALUE_HIDDEN])) {
309                        unset($event->result[$idx]);
310                        return;
311                    }
312                }
313            }
314        }
315
316    }
317
318
319    /**
320     * Handle the meta robots
321     * https://www.dokuwiki.org/devel:event:tpl_metaheader_output
322     * @param $event
323     * @param $param
324     */
325    function handleRobotsMeta(&$event, $param)
326    {
327
328        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
329        $requestedPath = $executionContext->getRequestedPath();
330
331        if (!FileSystems::exists($requestedPath)) {
332            return;
333        }
334
335        $page = MarkupPath::createPageFromPathObject($requestedPath);
336        try {
337            $follow = Robots::canBeIndexedAndGetFollowValue($page, $executionContext);
338        } catch (ExceptionNotEnabled $e) {
339            // Robots Protection is not Enabled
340            return;
341        }
342
343        // header ['X-Robots-Tag'] = 'noindex'; ???
344
345        foreach ($event->data['meta'] as $key => $meta) {
346            if (array_key_exists("name", $meta)) {
347                /**
348                 * We may have several properties
349                 */
350                if ($meta["name"] == "robots") {
351                    $event->data['meta'][$key]["content"] = "noindex,$follow";
352                }
353            }
354        }
355
356    }
357
358}
359