1 <?php
2 
3 require_once(__DIR__ . '/../vendor/autoload.php');
4 
5 use ComboStrap\ExceptionBadArgument;
6 use ComboStrap\ExceptionBadSyntax;
7 use ComboStrap\ExceptionNotEnabled;
8 use ComboStrap\ExecutionContext;
9 use ComboStrap\FetcherPage;
10 use ComboStrap\FileSystems;
11 use ComboStrap\Identity;
12 use ComboStrap\LogUtility;
13 use ComboStrap\LowQualityPage;
14 use ComboStrap\MarkupPath;
15 use ComboStrap\PageProtection;
16 use ComboStrap\PagePublicationDate;
17 use ComboStrap\Robots;
18 use ComboStrap\Web\Url;
19 use ComboStrap\WikiPath;
20 
21 
22 /**
23  *
24  */
25 class 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