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