1*45a874f4SNico<?php 2*45a874f4SNico 3*45a874f4SNiconamespace ComboStrap; 4*45a874f4SNico 5*45a874f4SNicouse ComboStrap\Meta\Field\AliasType; 6*45a874f4SNicouse ComboStrap\Web\Url; 7*45a874f4SNico 8*45a874f4SNicoclass Router 9*45a874f4SNico{ 10*45a874f4SNico 11*45a874f4SNico 12*45a874f4SNico public const GO_TO_SEARCH_ENGINE = 'GoToSearchEngine'; 13*45a874f4SNico public const GO_TO_NS_START_PAGE = 'GoToNsStartPage'; 14*45a874f4SNico public const GO_TO_EDIT_MODE = 'GoToEditMode'; 15*45a874f4SNico public const GO_TO_BEST_END_PAGE_NAME = 'GoToBestEndPageName'; 16*45a874f4SNico public const GO_TO_BEST_NAMESPACE = 'GoToBestNamespace'; 17*45a874f4SNico public const NOTHING = 'Nothing'; 18*45a874f4SNico public const GO_TO_BEST_PAGE_NAME = 'GoToBestPageName'; 19*45a874f4SNico private PageRules $pageRules; 20*45a874f4SNico 21*45a874f4SNico /** 22*45a874f4SNico * @throws ExceptionSqliteNotAvailable 23*45a874f4SNico * @throws ExceptionNotFound - no redirection found 24*45a874f4SNico */ 25*45a874f4SNico public function getRedirection(): RouterRedirection 26*45a874f4SNico { 27*45a874f4SNico 28*45a874f4SNico /** 29*45a874f4SNico * Without SQLite, this module does not work further 30*45a874f4SNico * It throws 31*45a874f4SNico */ 32*45a874f4SNico Sqlite::createOrGetSqlite(); 33*45a874f4SNico 34*45a874f4SNico /** 35*45a874f4SNico * Initiate Page Rules 36*45a874f4SNico */ 37*45a874f4SNico $this->pageRules = new PageRules(); 38*45a874f4SNico 39*45a874f4SNico 40*45a874f4SNico /** 41*45a874f4SNico * Unfortunately, DOKUWIKI_STARTED is not the first event 42*45a874f4SNico * The id may have been changed by 43*45a874f4SNico * {@link action_plugin_combo_lang::load_lang()} 44*45a874f4SNico * function, that's why we check against the {@link $_REQUEST} 45*45a874f4SNico * and not the global ID 46*45a874f4SNico */ 47*45a874f4SNico $originalId = self::getOriginalIdFromRequest(); 48*45a874f4SNico 49*45a874f4SNico /** 50*45a874f4SNico * Page is an existing id 51*45a874f4SNico * in the database ? 52*45a874f4SNico */ 53*45a874f4SNico global $ID; 54*45a874f4SNico $requestedMarkupPath = MarkupPath::createMarkupFromId($ID); 55*45a874f4SNico if (FileSystems::exists($requestedMarkupPath)) { 56*45a874f4SNico 57*45a874f4SNico /** 58*45a874f4SNico * If this is not the root home page 59*45a874f4SNico * and if the canonical id is the not the same (the id has changed) 60*45a874f4SNico * and if this is not a historical page (revision) 61*45a874f4SNico * redirect 62*45a874f4SNico */ 63*45a874f4SNico if ( 64*45a874f4SNico $originalId !== $requestedMarkupPath->getUrlId() // The id may have been changed 65*45a874f4SNico && $ID != Site::getIndexPageName() 66*45a874f4SNico && !isset($_REQUEST["rev"]) 67*45a874f4SNico ) { 68*45a874f4SNico /** 69*45a874f4SNico * TODO: When saving for the first time, the page is not stored in the database 70*45a874f4SNico * but that's not the case actually 71*45a874f4SNico */ 72*45a874f4SNico $databasePageRow = $requestedMarkupPath->getDatabasePage(); 73*45a874f4SNico if ($databasePageRow->exists()) { 74*45a874f4SNico /** 75*45a874f4SNico * A move may leave the database in a bad state, 76*45a874f4SNico * unfortunately (ie page is not in index, unable to update, ...) 77*45a874f4SNico * We test therefore if the database page id exists 78*45a874f4SNico */ 79*45a874f4SNico $targetPageId = $databasePageRow->getFromRow("id"); 80*45a874f4SNico $targetPath = MarkupPath::createMarkupFromId($targetPageId); 81*45a874f4SNico if (FileSystems::exists($targetPath)) { 82*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK_EXTENDED) 83*45a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 84*45a874f4SNico ->setTargetMarkupPath($targetPath) 85*45a874f4SNico ->build(); 86*45a874f4SNico } 87*45a874f4SNico 88*45a874f4SNico } 89*45a874f4SNico } 90*45a874f4SNico } 91*45a874f4SNico 92*45a874f4SNico $identifier = $ID; 93*45a874f4SNico 94*45a874f4SNico /** 95*45a874f4SNico * Page Id in the url 96*45a874f4SNico */ 97*45a874f4SNico $shortPageId = PageUrlPath::getShortEncodedPageIdFromUrlId($requestedMarkupPath->getPathObject()->getLastNameWithoutExtension()); 98*45a874f4SNico if ($shortPageId != null) { 99*45a874f4SNico $pageId = PageUrlPath::decodePageId($shortPageId); 100*45a874f4SNico } else { 101*45a874f4SNico /** 102*45a874f4SNico * Permalink with id 103*45a874f4SNico */ 104*45a874f4SNico $pageId = PageUrlPath::decodePageId($identifier); 105*45a874f4SNico } 106*45a874f4SNico if ($pageId !== null) { 107*45a874f4SNico 108*45a874f4SNico if ($requestedMarkupPath->getParent() === null) { 109*45a874f4SNico $page = DatabasePageRow::createFromPageId($pageId)->getMarkupPath(); 110*45a874f4SNico if ($page !== null && $page->exists()) { 111*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK) 112*45a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 113*45a874f4SNico ->setTargetMarkupPath($page) 114*45a874f4SNico ->build(); 115*45a874f4SNico } 116*45a874f4SNico } 117*45a874f4SNico 118*45a874f4SNico /** 119*45a874f4SNico * Page Id Abbr ? 120*45a874f4SNico * {@link PageUrlType::CONF_CANONICAL_URL_TYPE} 121*45a874f4SNico */ 122*45a874f4SNico $page = DatabasePageRow::createFromPageIdAbbr($pageId)->getMarkupPath(); 123*45a874f4SNico if ($page === null) { 124*45a874f4SNico // or the length of the abbr has changed 125*45a874f4SNico $canonicalDatabasePage = new DatabasePageRow(); 126*45a874f4SNico try { 127*45a874f4SNico $row = $canonicalDatabasePage->getDatabaseRowFromAttribute("substr(" . PageId::PROPERTY_NAME . ", 1, " . strlen($pageId) . ")", $pageId); 128*45a874f4SNico $canonicalDatabasePage->setRow($row); 129*45a874f4SNico $page = $canonicalDatabasePage->getMarkupPath(); 130*45a874f4SNico } catch (ExceptionNotFound $e) { 131*45a874f4SNico // nothing to do 132*45a874f4SNico } 133*45a874f4SNico } 134*45a874f4SNico if ($page !== null && $page->exists()) { 135*45a874f4SNico /** 136*45a874f4SNico * If the url canonical id has changed, we show it 137*45a874f4SNico * to the writer by performing a permanent redirect 138*45a874f4SNico */ 139*45a874f4SNico if ($identifier != $page->getUrlId()) { 140*45a874f4SNico // Google asks for a redirect 141*45a874f4SNico // https://developers.google.com/search/docs/advanced/crawling/301-redirects 142*45a874f4SNico // People access your site through several different URLs. 143*45a874f4SNico // If, for example, your home page can be reached in multiple ways 144*45a874f4SNico // (for instance, http://example.com/home, http://home.example.com, or http://www.example.com), 145*45a874f4SNico // it's a good idea to pick one of those URLs as your preferred (canonical) destination, 146*45a874f4SNico // and use redirects to send traffic from the other URLs to your preferred URL. 147*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK_EXTENDED) 148*45a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 149*45a874f4SNico ->setTargetMarkupPath($page) 150*45a874f4SNico ->build(); 151*45a874f4SNico 152*45a874f4SNico } 153*45a874f4SNico 154*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK_EXTENDED) 155*45a874f4SNico ->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD) 156*45a874f4SNico ->setTargetMarkupPath($page) 157*45a874f4SNico ->build(); 158*45a874f4SNico 159*45a874f4SNico } 160*45a874f4SNico // permanent url not yet in the database 161*45a874f4SNico // Other permanent such as permanent canonical ? 162*45a874f4SNico // We let the process go with the new identifier 163*45a874f4SNico 164*45a874f4SNico } 165*45a874f4SNico 166*45a874f4SNico /** 167*45a874f4SNico * Identifier is a Canonical ? 168*45a874f4SNico */ 169*45a874f4SNico $canonicalDatabasePage = DatabasePageRow::createFromCanonical($identifier); 170*45a874f4SNico $canonicalPage = $canonicalDatabasePage->getMarkupPath(); 171*45a874f4SNico if ($canonicalPage !== null && $canonicalPage->exists()) { 172*45a874f4SNico $builder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_CANONICAL) 173*45a874f4SNico ->setTargetMarkupPath($canonicalPage); 174*45a874f4SNico /** 175*45a874f4SNico * Does the canonical url is canonical name based 176*45a874f4SNico * ie {@link PageUrlType::CONF_VALUE_CANONICAL_PATH} 177*45a874f4SNico */ 178*45a874f4SNico if ($canonicalPage->getUrlId() === $identifier) { 179*45a874f4SNico $builder->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD); 180*45a874f4SNico } else { 181*45a874f4SNico $builder->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD); 182*45a874f4SNico } 183*45a874f4SNico return $builder->build(); 184*45a874f4SNico 185*45a874f4SNico } 186*45a874f4SNico 187*45a874f4SNico /** 188*45a874f4SNico * Identifier is an alias 189*45a874f4SNico */ 190*45a874f4SNico $aliasRequestedPage = DatabasePageRow::createFromAlias($identifier)->getMarkupPath(); 191*45a874f4SNico if ( 192*45a874f4SNico $aliasRequestedPage !== null 193*45a874f4SNico && $aliasRequestedPage->exists() 194*45a874f4SNico // The build alias is the file system metadata alias 195*45a874f4SNico // it may be null if the replication in the database was not successful 196*45a874f4SNico && $aliasRequestedPage->getBuildAlias() !== null 197*45a874f4SNico ) { 198*45a874f4SNico $buildAlias = $aliasRequestedPage->getBuildAlias(); 199*45a874f4SNico $builder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_ALIAS) 200*45a874f4SNico ->setTargetMarkupPath($aliasRequestedPage); 201*45a874f4SNico switch ($buildAlias->getType()) { 202*45a874f4SNico case AliasType::REDIRECT: 203*45a874f4SNico return $builder->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD)->build(); 204*45a874f4SNico case AliasType::SYNONYM: 205*45a874f4SNico return $builder->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD)->build(); 206*45a874f4SNico default: 207*45a874f4SNico LogUtility::msg("The alias type ({$buildAlias->getType()}) is unknown. A permanent redirect was performed for the alias $identifier"); 208*45a874f4SNico return $builder->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD)->build(); 209*45a874f4SNico } 210*45a874f4SNico } 211*45a874f4SNico 212*45a874f4SNico /** 213*45a874f4SNico * Do we have a page rules 214*45a874f4SNico * If there is a redirection defined in the page rules 215*45a874f4SNico */ 216*45a874f4SNico try { 217*45a874f4SNico return $this->getRedirectionFromPageRules(); 218*45a874f4SNico } catch (ExceptionNotFound $e) { 219*45a874f4SNico // no pages rules redirection 220*45a874f4SNico } 221*45a874f4SNico 222*45a874f4SNico /** 223*45a874f4SNico * No redirection found in the database by id 224*45a874f4SNico */ 225*45a874f4SNico 226*45a874f4SNico /** 227*45a874f4SNico * Edit mode 228*45a874f4SNico */ 229*45a874f4SNico $conf = ExecutionContext::getActualOrCreateFromEnv()->getConfig(); 230*45a874f4SNico if (Identity::isWriter() && $conf->getBooleanValue(self::GO_TO_EDIT_MODE, true)) { 231*45a874f4SNico 232*45a874f4SNico // Stop here 233*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(self::GO_TO_EDIT_MODE) 234*45a874f4SNico ->build(); 235*45a874f4SNico 236*45a874f4SNico } 237*45a874f4SNico 238*45a874f4SNico /** 239*45a874f4SNico * We are still a reader, the redirection does not exist the user is not allowed to edit the page (public of other) 240*45a874f4SNico */ 241*45a874f4SNico $actionReaderFirst = $conf->getValue('ActionReaderFirst'); 242*45a874f4SNico if ($actionReaderFirst == self::NOTHING) { 243*45a874f4SNico throw new ExceptionNotFound(); 244*45a874f4SNico } 245*45a874f4SNico 246*45a874f4SNico // We are reader and their is no redirection set, we apply the algorithm 247*45a874f4SNico $readerAlgorithms = array(); 248*45a874f4SNico $readerAlgorithms[0] = $actionReaderFirst; 249*45a874f4SNico $readerAlgorithms[1] = $conf->getValue('ActionReaderSecond'); 250*45a874f4SNico $readerAlgorithms[2] = $conf->getValue('ActionReaderThird'); 251*45a874f4SNico 252*45a874f4SNico while ( 253*45a874f4SNico ($algorithm = array_shift($readerAlgorithms)) != null 254*45a874f4SNico ) { 255*45a874f4SNico 256*45a874f4SNico switch ($algorithm) { 257*45a874f4SNico 258*45a874f4SNico case self::NOTHING: 259*45a874f4SNico throw new ExceptionNotFound(); 260*45a874f4SNico 261*45a874f4SNico case self::GO_TO_BEST_END_PAGE_NAME: 262*45a874f4SNico 263*45a874f4SNico /** 264*45a874f4SNico * @var MarkupPath $bestEndPage 265*45a874f4SNico */ 266*45a874f4SNico list($bestEndPage, $method) = RouterBestEndPage::process($requestedMarkupPath); 267*45a874f4SNico if ($bestEndPage != null) { 268*45a874f4SNico try { 269*45a874f4SNico $notSamePage = $bestEndPage->getWikiId() !== $requestedMarkupPath->getWikiId(); 270*45a874f4SNico } catch (ExceptionBadArgument $e) { 271*45a874f4SNico LogUtility::error("The path should be wiki markup path", LogUtility::SUPPORT_CANONICAL, $e); 272*45a874f4SNico $notSamePage = false; 273*45a874f4SNico } 274*45a874f4SNico if ($notSamePage) { 275*45a874f4SNico $redirectionBuilder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_BEST_END_PAGE_NAME) 276*45a874f4SNico ->setTargetMarkupPath($bestEndPage); 277*45a874f4SNico switch ($method) { 278*45a874f4SNico case RouterRedirection::REDIRECT_PERMANENT_METHOD: 279*45a874f4SNico return $redirectionBuilder 280*45a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 281*45a874f4SNico ->build(); 282*45a874f4SNico case RouterRedirection::REDIRECT_NOTFOUND_METHOD: 283*45a874f4SNico return $redirectionBuilder 284*45a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 285*45a874f4SNico ->build(); 286*45a874f4SNico default: 287*45a874f4SNico LogUtility::error("This redirection method ($method) was not expected for the redirection algorithm ($algorithm)"); 288*45a874f4SNico } 289*45a874f4SNico } 290*45a874f4SNico 291*45a874f4SNico } 292*45a874f4SNico break; 293*45a874f4SNico 294*45a874f4SNico case self::GO_TO_NS_START_PAGE: 295*45a874f4SNico 296*45a874f4SNico $redirectBuilder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_START_PAGE) 297*45a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD); 298*45a874f4SNico 299*45a874f4SNico // Start page with the conf['start'] parameter 300*45a874f4SNico $startPage = getNS($identifier) . ':' . $conf['start']; 301*45a874f4SNico $startPath = MarkupPath::createMarkupFromId($startPage); 302*45a874f4SNico if (FileSystems::exists($startPath)) { 303*45a874f4SNico return $redirectBuilder->setTargetMarkupPath($startPath)->build(); 304*45a874f4SNico } 305*45a874f4SNico 306*45a874f4SNico // Start page with the same name than the namespace 307*45a874f4SNico $startPage = getNS($identifier) . ':' . curNS($identifier); 308*45a874f4SNico $startPath = MarkupPath::createMarkupFromId($startPage); 309*45a874f4SNico if (FileSystems::exists($startPath)) { 310*45a874f4SNico return $redirectBuilder->setTargetMarkupPath($startPath)->build(); 311*45a874f4SNico } 312*45a874f4SNico 313*45a874f4SNico break; 314*45a874f4SNico 315*45a874f4SNico case self::GO_TO_BEST_PAGE_NAME: 316*45a874f4SNico 317*45a874f4SNico $bestPageId = null; 318*45a874f4SNico 319*45a874f4SNico $bestPage = $this->getBestPage($identifier); 320*45a874f4SNico $bestPageId = $bestPage['id']; 321*45a874f4SNico $scorePageName = $bestPage['score']; 322*45a874f4SNico 323*45a874f4SNico // Get Score from a Namespace 324*45a874f4SNico $bestNamespace = $this->scoreBestNamespace($identifier); 325*45a874f4SNico $bestNamespaceId = $bestNamespace['namespace']; 326*45a874f4SNico $namespaceScore = $bestNamespace['score']; 327*45a874f4SNico 328*45a874f4SNico // Compare the two score 329*45a874f4SNico if ($scorePageName > 0 or $namespaceScore > 0) { 330*45a874f4SNico $redirectionBuilder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_BEST_PAGE_NAME) 331*45a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD); 332*45a874f4SNico if ($scorePageName > $namespaceScore) { 333*45a874f4SNico return $redirectionBuilder 334*45a874f4SNico ->setTargetMarkupPath(MarkupPath::createMarkupFromId($bestPageId)) 335*45a874f4SNico ->build(); 336*45a874f4SNico } 337*45a874f4SNico return $redirectionBuilder 338*45a874f4SNico ->setTargetMarkupPath(MarkupPath::createMarkupFromId($bestNamespaceId)) 339*45a874f4SNico ->build(); 340*45a874f4SNico } 341*45a874f4SNico break; 342*45a874f4SNico 343*45a874f4SNico case self::GO_TO_BEST_NAMESPACE: 344*45a874f4SNico 345*45a874f4SNico $scoreNamespace = $this->scoreBestNamespace($identifier); 346*45a874f4SNico $bestNamespaceId = $scoreNamespace['namespace']; 347*45a874f4SNico $score = $scoreNamespace['score']; 348*45a874f4SNico 349*45a874f4SNico if ($score > 0) { 350*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_BEST_NAMESPACE) 351*45a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 352*45a874f4SNico ->setTargetMarkupPath(MarkupPath::createMarkupFromId($bestNamespaceId)) 353*45a874f4SNico ->build(); 354*45a874f4SNico } 355*45a874f4SNico break; 356*45a874f4SNico 357*45a874f4SNico case self::GO_TO_SEARCH_ENGINE: 358*45a874f4SNico 359*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_SEARCH_ENGINE) 360*45a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 361*45a874f4SNico ->build(); 362*45a874f4SNico 363*45a874f4SNico } 364*45a874f4SNico 365*45a874f4SNico } 366*45a874f4SNico 367*45a874f4SNico throw new ExceptionNotFound(); 368*45a874f4SNico 369*45a874f4SNico } 370*45a874f4SNico 371*45a874f4SNico 372*45a874f4SNico /** 373*45a874f4SNico * @return string|null 374*45a874f4SNico * 375*45a874f4SNico * Return the original id from the request 376*45a874f4SNico * ie `howto:how-to-get-started-with-combostrap-m3i8vga8` 377*45a874f4SNico * if `/howto/how-to-get-started-with-combostrap-m3i8vga8` 378*45a874f4SNico * 379*45a874f4SNico * Unfortunately, DOKUWIKI_STARTED is not the first event 380*45a874f4SNico * The id may have been changed by 381*45a874f4SNico * {@link action_plugin_combo_lang::load_lang()} 382*45a874f4SNico * function, that's why we have this function 383*45a874f4SNico * to get the original requested id 384*45a874f4SNico */ 385*45a874f4SNico static function getOriginalIdFromRequest(): ?string 386*45a874f4SNico { 387*45a874f4SNico $originalId = $_GET["id"] ?? null; 388*45a874f4SNico if ($originalId === null) { 389*45a874f4SNico return null; 390*45a874f4SNico } 391*45a874f4SNico // We may get a `/` as first character 392*45a874f4SNico // because we return an id, we need to delete it 393*45a874f4SNico if (substr($originalId, 0, 1) === "/") { 394*45a874f4SNico $originalId = substr($originalId, 1); 395*45a874f4SNico } 396*45a874f4SNico // transform / to : 397*45a874f4SNico return str_replace("/", WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $originalId); 398*45a874f4SNico } 399*45a874f4SNico 400*45a874f4SNico /** 401*45a874f4SNico * Return a redirection declared in the redirection table or throw if not found 402*45a874f4SNico * @throws ExceptionNotFound 403*45a874f4SNico */ 404*45a874f4SNico private function getRedirectionFromPageRules(): RouterRedirection 405*45a874f4SNico { 406*45a874f4SNico global $ID; 407*45a874f4SNico 408*45a874f4SNico $calculatedTarget = null; 409*45a874f4SNico $ruleMatcher = null; // Used in a warning message if the target page does not exist 410*45a874f4SNico // Known redirection in the table 411*45a874f4SNico // Get the page from redirection data 412*45a874f4SNico $rules = $this->pageRules->getRules(); 413*45a874f4SNico foreach ($rules as $rule) { 414*45a874f4SNico 415*45a874f4SNico $ruleMatcher = strtolower($rule[PageRules::MATCHER_NAME]); 416*45a874f4SNico $ruleTarget = $rule[PageRules::TARGET_NAME]; 417*45a874f4SNico 418*45a874f4SNico // Glob to Rexgexp 419*45a874f4SNico $regexpPattern = '/' . str_replace("*", "(.*)", $ruleMatcher) . '/i'; 420*45a874f4SNico 421*45a874f4SNico // Match ? 422*45a874f4SNico // https://www.php.net/manual/en/function.preg-match.php 423*45a874f4SNico $pregMatchResult = @preg_match($regexpPattern, $ID, $matches); 424*45a874f4SNico if ($pregMatchResult === false) { 425*45a874f4SNico // The `if` to take into account this problem 426*45a874f4SNico // PHP Warning: preg_match(): Unknown modifier 'd' in /opt/www/datacadamia.com/lib/plugins/combo/action/router.php on line 972 427*45a874f4SNico LogUtility::log2file("processing Page Rules An error occurred with the pattern ($regexpPattern)", LogUtility::LVL_MSG_WARNING); 428*45a874f4SNico throw new ExceptionNotFound(); 429*45a874f4SNico } 430*45a874f4SNico if ($pregMatchResult) { 431*45a874f4SNico $calculatedTarget = $ruleTarget; 432*45a874f4SNico foreach ($matches as $key => $match) { 433*45a874f4SNico if ($key == 0) { 434*45a874f4SNico continue; 435*45a874f4SNico } else { 436*45a874f4SNico $calculatedTarget = str_replace('$' . $key, $match, $calculatedTarget); 437*45a874f4SNico } 438*45a874f4SNico } 439*45a874f4SNico break; 440*45a874f4SNico } 441*45a874f4SNico } 442*45a874f4SNico 443*45a874f4SNico if ($calculatedTarget == null) { 444*45a874f4SNico throw new ExceptionNotFound(); 445*45a874f4SNico } 446*45a874f4SNico 447*45a874f4SNico // If this is an external redirect (other domain) 448*45a874f4SNico try { 449*45a874f4SNico $url = Url::createFromString($calculatedTarget); 450*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PAGE_RULES) 451*45a874f4SNico ->setTargetUrl($url) 452*45a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 453*45a874f4SNico ->build(); 454*45a874f4SNico } catch (ExceptionBadSyntax|ExceptionBadArgument $e) { 455*45a874f4SNico // not an URL 456*45a874f4SNico } 457*45a874f4SNico 458*45a874f4SNico 459*45a874f4SNico // If the page exist 460*45a874f4SNico // This is DokuWiki Id and should always be lowercase 461*45a874f4SNico // The page rule may have change that 462*45a874f4SNico $calculatedTarget = strtolower($calculatedTarget); 463*45a874f4SNico $markupPath = MarkupPath::createMarkupFromId($calculatedTarget); 464*45a874f4SNico if (FileSystems::exists($markupPath)) { 465*45a874f4SNico 466*45a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PAGE_RULES) 467*45a874f4SNico ->setTargetMarkupPath($markupPath) 468*45a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 469*45a874f4SNico ->build(); 470*45a874f4SNico 471*45a874f4SNico } 472*45a874f4SNico 473*45a874f4SNico LogUtility::error("The calculated target page ($calculatedTarget) (for the non-existing page `$ID` with the matcher `$ruleMatcher`) does not exist"); 474*45a874f4SNico throw new ExceptionNotFound(); 475*45a874f4SNico 476*45a874f4SNico } 477*45a874f4SNico 478*45a874f4SNico 479*45a874f4SNico /** 480*45a874f4SNico * @param $id 481*45a874f4SNico * @return array 482*45a874f4SNico */ 483*45a874f4SNico private 484*45a874f4SNico function getBestPage($id): array 485*45a874f4SNico { 486*45a874f4SNico 487*45a874f4SNico // The return parameters 488*45a874f4SNico $bestPageId = null; 489*45a874f4SNico $scorePageName = null; 490*45a874f4SNico 491*45a874f4SNico // Get Score from a page 492*45a874f4SNico $pageName = noNS($id); 493*45a874f4SNico $pagesWithSameName = ft_pageLookup($pageName); 494*45a874f4SNico if (count($pagesWithSameName) > 0) { 495*45a874f4SNico 496*45a874f4SNico // Search same namespace in the page found than in the Id page asked. 497*45a874f4SNico $bestNbWordFound = 0; 498*45a874f4SNico 499*45a874f4SNico 500*45a874f4SNico $wordsInPageSourceId = explode(':', $id); 501*45a874f4SNico foreach ($pagesWithSameName as $targetPageId => $title) { 502*45a874f4SNico 503*45a874f4SNico // Nb of word found in the target page id 504*45a874f4SNico // that are in the source page id 505*45a874f4SNico $nbWordFound = 0; 506*45a874f4SNico foreach ($wordsInPageSourceId as $word) { 507*45a874f4SNico $nbWordFound = $nbWordFound + substr_count($targetPageId, $word); 508*45a874f4SNico } 509*45a874f4SNico 510*45a874f4SNico if ($bestPageId == null) { 511*45a874f4SNico 512*45a874f4SNico $bestNbWordFound = $nbWordFound; 513*45a874f4SNico $bestPageId = $targetPageId; 514*45a874f4SNico 515*45a874f4SNico } else { 516*45a874f4SNico 517*45a874f4SNico if ($nbWordFound >= $bestNbWordFound && strlen($bestPageId) > strlen($targetPageId)) { 518*45a874f4SNico 519*45a874f4SNico $bestNbWordFound = $nbWordFound; 520*45a874f4SNico $bestPageId = $targetPageId; 521*45a874f4SNico 522*45a874f4SNico } 523*45a874f4SNico 524*45a874f4SNico } 525*45a874f4SNico 526*45a874f4SNico } 527*45a874f4SNico $config = ExecutionContext::getActualOrCreateFromEnv()->getConfig(); 528*45a874f4SNico $weightFactorForSamePageName = $config->getValue('WeightFactorForSamePageName'); 529*45a874f4SNico $weightFactorForSameNamespace = $config->getValue('WeightFactorForSameNamespace'); 530*45a874f4SNico $scorePageName = $weightFactorForSamePageName + ($bestNbWordFound - 1) * $weightFactorForSameNamespace; 531*45a874f4SNico return array( 532*45a874f4SNico 'id' => $bestPageId, 533*45a874f4SNico 'score' => $scorePageName); 534*45a874f4SNico } 535*45a874f4SNico return array( 536*45a874f4SNico 'id' => $bestPageId, 537*45a874f4SNico 'score' => $scorePageName 538*45a874f4SNico ); 539*45a874f4SNico 540*45a874f4SNico } 541*45a874f4SNico 542*45a874f4SNico /** 543*45a874f4SNico * getBestNamespace 544*45a874f4SNico * Return a list with 'BestNamespaceId Score' 545*45a874f4SNico * @param $id 546*45a874f4SNico * @return array 547*45a874f4SNico */ 548*45a874f4SNico private 549*45a874f4SNico function scoreBestNamespace($id): array 550*45a874f4SNico { 551*45a874f4SNico 552*45a874f4SNico $nameSpaces = array(); 553*45a874f4SNico $pathNames = array(); 554*45a874f4SNico 555*45a874f4SNico // Parameters 556*45a874f4SNico $requestedPath = MarkupPath::createMarkupFromId($id); 557*45a874f4SNico try { 558*45a874f4SNico $pageNameSpace = $requestedPath->getParent(); 559*45a874f4SNico $pathNames = array_slice($pageNameSpace->getNames(), 0, -1); 560*45a874f4SNico if (FileSystems::exists($pageNameSpace)) { 561*45a874f4SNico $nameSpaces = array($pageNameSpace->toAbsoluteId()); 562*45a874f4SNico } else { 563*45a874f4SNico global $conf; 564*45a874f4SNico $nameSpaces = ft_pageLookup($conf['start']); 565*45a874f4SNico } 566*45a874f4SNico } catch (ExceptionNotFound $e) { 567*45a874f4SNico // no parent, root 568*45a874f4SNico } 569*45a874f4SNico 570*45a874f4SNico // Parameters and search the best namespace 571*45a874f4SNico $bestNbWordFound = 0; 572*45a874f4SNico $bestNamespaceId = null; 573*45a874f4SNico foreach ($nameSpaces as $nameSpace) { 574*45a874f4SNico 575*45a874f4SNico $nbWordFound = 0; 576*45a874f4SNico foreach ($pathNames as $pathName) { 577*45a874f4SNico if (strlen($pathName) > 2) { 578*45a874f4SNico $nbWordFound = $nbWordFound + substr_count($nameSpace, $pathName); 579*45a874f4SNico } 580*45a874f4SNico } 581*45a874f4SNico if ($nbWordFound > $bestNbWordFound) { 582*45a874f4SNico // Take only the smallest namespace 583*45a874f4SNico if ($bestNbWordFound == null || strlen($nameSpace) < strlen($bestNamespaceId)) { 584*45a874f4SNico $bestNbWordFound = $nbWordFound; 585*45a874f4SNico $bestNamespaceId = $nameSpace; 586*45a874f4SNico } 587*45a874f4SNico } 588*45a874f4SNico } 589*45a874f4SNico $config = ExecutionContext::getActualOrCreateFromEnv()->getConfig(); 590*45a874f4SNico $startPageFactor = $config->getValue('WeightFactorForStartPage'); 591*45a874f4SNico $nameSpaceFactor = $config->getValue('WeightFactorForSameNamespace'); 592*45a874f4SNico if ($bestNbWordFound > 0) { 593*45a874f4SNico $bestNamespaceScore = $bestNbWordFound * $nameSpaceFactor + $startPageFactor; 594*45a874f4SNico } else { 595*45a874f4SNico $bestNamespaceScore = 0; 596*45a874f4SNico } 597*45a874f4SNico 598*45a874f4SNico 599*45a874f4SNico return array( 600*45a874f4SNico 'namespace' => $bestNamespaceId, 601*45a874f4SNico 'score' => $bestNamespaceScore 602*45a874f4SNico ); 603*45a874f4SNico 604*45a874f4SNico } 605*45a874f4SNico 606*45a874f4SNico 607*45a874f4SNico} 608