145a874f4SNico<?php 245a874f4SNico 345a874f4SNiconamespace ComboStrap; 445a874f4SNico 545a874f4SNicouse ComboStrap\Meta\Field\AliasType; 645a874f4SNicouse ComboStrap\Web\Url; 745a874f4SNico 845a874f4SNicoclass Router 945a874f4SNico{ 1045a874f4SNico 1145a874f4SNico 1245a874f4SNico public const GO_TO_SEARCH_ENGINE = 'GoToSearchEngine'; 1345a874f4SNico public const GO_TO_NS_START_PAGE = 'GoToNsStartPage'; 1445a874f4SNico public const GO_TO_EDIT_MODE = 'GoToEditMode'; 1545a874f4SNico public const GO_TO_BEST_END_PAGE_NAME = 'GoToBestEndPageName'; 1645a874f4SNico public const GO_TO_BEST_NAMESPACE = 'GoToBestNamespace'; 1745a874f4SNico public const NOTHING = 'Nothing'; 1845a874f4SNico public const GO_TO_BEST_PAGE_NAME = 'GoToBestPageName'; 1945a874f4SNico private PageRules $pageRules; 2045a874f4SNico 2145a874f4SNico /** 2245a874f4SNico * @throws ExceptionSqliteNotAvailable 2345a874f4SNico * @throws ExceptionNotFound - no redirection found 2445a874f4SNico */ 2545a874f4SNico public function getRedirection(): RouterRedirection 2645a874f4SNico { 2745a874f4SNico 2845a874f4SNico /** 2945a874f4SNico * Without SQLite, this module does not work further 3045a874f4SNico * It throws 3145a874f4SNico */ 3245a874f4SNico Sqlite::createOrGetSqlite(); 3345a874f4SNico 3445a874f4SNico /** 3545a874f4SNico * Initiate Page Rules 3645a874f4SNico */ 3745a874f4SNico $this->pageRules = new PageRules(); 3845a874f4SNico 3945a874f4SNico 4045a874f4SNico /** 4145a874f4SNico * Unfortunately, DOKUWIKI_STARTED is not the first event 4245a874f4SNico * The id may have been changed by 4345a874f4SNico * {@link action_plugin_combo_lang::load_lang()} 4445a874f4SNico * function, that's why we check against the {@link $_REQUEST} 4545a874f4SNico * and not the global ID 4645a874f4SNico */ 4745a874f4SNico $originalId = self::getOriginalIdFromRequest(); 4845a874f4SNico 4945a874f4SNico /** 5045a874f4SNico * Page is an existing id 5145a874f4SNico * in the database ? 5245a874f4SNico */ 5345a874f4SNico global $ID; 5445a874f4SNico $requestedMarkupPath = MarkupPath::createMarkupFromId($ID); 5545a874f4SNico if (FileSystems::exists($requestedMarkupPath)) { 5645a874f4SNico 5745a874f4SNico /** 5845a874f4SNico * If this is not the root home page 5945a874f4SNico * and if the canonical id is the not the same (the id has changed) 6045a874f4SNico * and if this is not a historical page (revision) 6145a874f4SNico * redirect 6245a874f4SNico */ 6345a874f4SNico if ( 6445a874f4SNico $originalId !== $requestedMarkupPath->getUrlId() // The id may have been changed 6545a874f4SNico && $ID != Site::getIndexPageName() 6645a874f4SNico && !isset($_REQUEST["rev"]) 6745a874f4SNico ) { 6845a874f4SNico /** 6945a874f4SNico * TODO: When saving for the first time, the page is not stored in the database 7045a874f4SNico * but that's not the case actually 7145a874f4SNico */ 7245a874f4SNico $databasePageRow = $requestedMarkupPath->getDatabasePage(); 7345a874f4SNico if ($databasePageRow->exists()) { 7445a874f4SNico /** 7545a874f4SNico * A move may leave the database in a bad state, 7645a874f4SNico * unfortunately (ie page is not in index, unable to update, ...) 7745a874f4SNico * We test therefore if the database page id exists 7845a874f4SNico */ 7945a874f4SNico $targetPageId = $databasePageRow->getFromRow("id"); 8045a874f4SNico $targetPath = MarkupPath::createMarkupFromId($targetPageId); 8145a874f4SNico if (FileSystems::exists($targetPath)) { 8245a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK_EXTENDED) 8345a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 8445a874f4SNico ->setTargetMarkupPath($targetPath) 8545a874f4SNico ->build(); 8645a874f4SNico } 8745a874f4SNico 8845a874f4SNico } 8945a874f4SNico } 9045a874f4SNico } 9145a874f4SNico 9245a874f4SNico $identifier = $ID; 9345a874f4SNico 9445a874f4SNico /** 9545a874f4SNico * Page Id in the url 96ecf8d738SNico * Note that if the ID is a permalink, global $ID has already the real id 97ecf8d738SNico * Why? because unfortunately, DOKUWIKI_STARTED is not the first event 98ecf8d738SNico * {@link action_plugin_combo_lang::load_lang()} may have already 99ecf8d738SNico * transformed a permalink into a real dokuwiki id 100ecf8d738SNico * 101ecf8d738SNico * We let it here because we don't know for sure that it will stay this way 102ecf8d738SNico * What fucked up is fucked up 10345a874f4SNico */ 10445a874f4SNico $shortPageId = PageUrlPath::getShortEncodedPageIdFromUrlId($requestedMarkupPath->getPathObject()->getLastNameWithoutExtension()); 10545a874f4SNico if ($shortPageId != null) { 10645a874f4SNico $pageId = PageUrlPath::decodePageId($shortPageId); 10745a874f4SNico } else { 10845a874f4SNico /** 10945a874f4SNico * Permalink with id 11045a874f4SNico */ 11145a874f4SNico $pageId = PageUrlPath::decodePageId($identifier); 11245a874f4SNico } 11345a874f4SNico if ($pageId !== null) { 11445a874f4SNico 11545a874f4SNico if ($requestedMarkupPath->getParent() === null) { 11645a874f4SNico $page = DatabasePageRow::createFromPageId($pageId)->getMarkupPath(); 11745a874f4SNico if ($page !== null && $page->exists()) { 11845a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK) 11945a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 12045a874f4SNico ->setTargetMarkupPath($page) 12145a874f4SNico ->build(); 12245a874f4SNico } 12345a874f4SNico } 12445a874f4SNico 12545a874f4SNico /** 12645a874f4SNico * Page Id Abbr ? 12745a874f4SNico * {@link PageUrlType::CONF_CANONICAL_URL_TYPE} 12845a874f4SNico */ 12945a874f4SNico $page = DatabasePageRow::createFromPageIdAbbr($pageId)->getMarkupPath(); 13045a874f4SNico if ($page === null) { 13145a874f4SNico // or the length of the abbr has changed 13245a874f4SNico $canonicalDatabasePage = new DatabasePageRow(); 13345a874f4SNico try { 13445a874f4SNico $row = $canonicalDatabasePage->getDatabaseRowFromAttribute("substr(" . PageId::PROPERTY_NAME . ", 1, " . strlen($pageId) . ")", $pageId); 13545a874f4SNico $canonicalDatabasePage->setRow($row); 13645a874f4SNico $page = $canonicalDatabasePage->getMarkupPath(); 13745a874f4SNico } catch (ExceptionNotFound $e) { 13845a874f4SNico // nothing to do 13945a874f4SNico } 14045a874f4SNico } 14145a874f4SNico if ($page !== null && $page->exists()) { 14245a874f4SNico /** 14345a874f4SNico * If the url canonical id has changed, we show it 14445a874f4SNico * to the writer by performing a permanent redirect 14545a874f4SNico */ 14645a874f4SNico if ($identifier != $page->getUrlId()) { 14745a874f4SNico // Google asks for a redirect 14845a874f4SNico // https://developers.google.com/search/docs/advanced/crawling/301-redirects 14945a874f4SNico // People access your site through several different URLs. 15045a874f4SNico // If, for example, your home page can be reached in multiple ways 15145a874f4SNico // (for instance, http://example.com/home, http://home.example.com, or http://www.example.com), 15245a874f4SNico // it's a good idea to pick one of those URLs as your preferred (canonical) destination, 15345a874f4SNico // and use redirects to send traffic from the other URLs to your preferred URL. 15445a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK_EXTENDED) 15545a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 15645a874f4SNico ->setTargetMarkupPath($page) 15745a874f4SNico ->build(); 15845a874f4SNico 15945a874f4SNico } 16045a874f4SNico 16145a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PERMALINK_EXTENDED) 16245a874f4SNico ->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD) 16345a874f4SNico ->setTargetMarkupPath($page) 16445a874f4SNico ->build(); 16545a874f4SNico 16645a874f4SNico } 16745a874f4SNico // permanent url not yet in the database 16845a874f4SNico // Other permanent such as permanent canonical ? 16945a874f4SNico // We let the process go with the new identifier 17045a874f4SNico 17145a874f4SNico } 17245a874f4SNico 17345a874f4SNico /** 17445a874f4SNico * Identifier is a Canonical ? 17545a874f4SNico */ 17645a874f4SNico $canonicalDatabasePage = DatabasePageRow::createFromCanonical($identifier); 17745a874f4SNico $canonicalPage = $canonicalDatabasePage->getMarkupPath(); 17845a874f4SNico if ($canonicalPage !== null && $canonicalPage->exists()) { 17945a874f4SNico $builder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_CANONICAL) 18045a874f4SNico ->setTargetMarkupPath($canonicalPage); 18145a874f4SNico /** 18245a874f4SNico * Does the canonical url is canonical name based 18345a874f4SNico * ie {@link PageUrlType::CONF_VALUE_CANONICAL_PATH} 18445a874f4SNico */ 18545a874f4SNico if ($canonicalPage->getUrlId() === $identifier) { 18645a874f4SNico $builder->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD); 18745a874f4SNico } else { 18845a874f4SNico $builder->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD); 18945a874f4SNico } 19045a874f4SNico return $builder->build(); 19145a874f4SNico 19245a874f4SNico } 19345a874f4SNico 19445a874f4SNico /** 19545a874f4SNico * Identifier is an alias 19645a874f4SNico */ 19745a874f4SNico $aliasRequestedPage = DatabasePageRow::createFromAlias($identifier)->getMarkupPath(); 19845a874f4SNico if ( 19945a874f4SNico $aliasRequestedPage !== null 20045a874f4SNico && $aliasRequestedPage->exists() 20145a874f4SNico // The build alias is the file system metadata alias 20245a874f4SNico // it may be null if the replication in the database was not successful 20345a874f4SNico && $aliasRequestedPage->getBuildAlias() !== null 20445a874f4SNico ) { 20545a874f4SNico $buildAlias = $aliasRequestedPage->getBuildAlias(); 20645a874f4SNico $builder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_ALIAS) 20745a874f4SNico ->setTargetMarkupPath($aliasRequestedPage); 20845a874f4SNico switch ($buildAlias->getType()) { 20945a874f4SNico case AliasType::REDIRECT: 21045a874f4SNico return $builder->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD)->build(); 21145a874f4SNico case AliasType::SYNONYM: 21245a874f4SNico return $builder->setType(RouterRedirection::REDIRECT_TRANSPARENT_METHOD)->build(); 21345a874f4SNico default: 21445a874f4SNico LogUtility::msg("The alias type ({$buildAlias->getType()}) is unknown. A permanent redirect was performed for the alias $identifier"); 21545a874f4SNico return $builder->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD)->build(); 21645a874f4SNico } 21745a874f4SNico } 21845a874f4SNico 21945a874f4SNico /** 22045a874f4SNico * Do we have a page rules 22145a874f4SNico * If there is a redirection defined in the page rules 22245a874f4SNico */ 22345a874f4SNico try { 22445a874f4SNico return $this->getRedirectionFromPageRules(); 22545a874f4SNico } catch (ExceptionNotFound $e) { 22645a874f4SNico // no pages rules redirection 22745a874f4SNico } 22845a874f4SNico 22945a874f4SNico /** 23045a874f4SNico * No redirection found in the database by id 23145a874f4SNico */ 23245a874f4SNico 23345a874f4SNico /** 23445a874f4SNico * Edit mode 23545a874f4SNico */ 23645a874f4SNico $conf = ExecutionContext::getActualOrCreateFromEnv()->getConfig(); 23745a874f4SNico if (Identity::isWriter() && $conf->getBooleanValue(self::GO_TO_EDIT_MODE, true)) { 23845a874f4SNico 23945a874f4SNico // Stop here 24045a874f4SNico return RouterRedirectionBuilder::createFromOrigin(self::GO_TO_EDIT_MODE) 24145a874f4SNico ->build(); 24245a874f4SNico 24345a874f4SNico } 24445a874f4SNico 24545a874f4SNico /** 24645a874f4SNico * We are still a reader, the redirection does not exist the user is not allowed to edit the page (public of other) 24745a874f4SNico */ 24845a874f4SNico $actionReaderFirst = $conf->getValue('ActionReaderFirst'); 24945a874f4SNico if ($actionReaderFirst == self::NOTHING) { 25045a874f4SNico throw new ExceptionNotFound(); 25145a874f4SNico } 25245a874f4SNico 25345a874f4SNico // We are reader and their is no redirection set, we apply the algorithm 25445a874f4SNico $readerAlgorithms = array(); 25545a874f4SNico $readerAlgorithms[0] = $actionReaderFirst; 25645a874f4SNico $readerAlgorithms[1] = $conf->getValue('ActionReaderSecond'); 25745a874f4SNico $readerAlgorithms[2] = $conf->getValue('ActionReaderThird'); 25845a874f4SNico 25945a874f4SNico while ( 26045a874f4SNico ($algorithm = array_shift($readerAlgorithms)) != null 26145a874f4SNico ) { 26245a874f4SNico 26345a874f4SNico switch ($algorithm) { 26445a874f4SNico 26545a874f4SNico case self::NOTHING: 26645a874f4SNico throw new ExceptionNotFound(); 26745a874f4SNico 26845a874f4SNico case self::GO_TO_BEST_END_PAGE_NAME: 26945a874f4SNico 27045a874f4SNico /** 27145a874f4SNico * @var MarkupPath $bestEndPage 27245a874f4SNico */ 27345a874f4SNico list($bestEndPage, $method) = RouterBestEndPage::process($requestedMarkupPath); 27445a874f4SNico if ($bestEndPage != null) { 27545a874f4SNico try { 27645a874f4SNico $notSamePage = $bestEndPage->getWikiId() !== $requestedMarkupPath->getWikiId(); 27745a874f4SNico } catch (ExceptionBadArgument $e) { 27845a874f4SNico LogUtility::error("The path should be wiki markup path", LogUtility::SUPPORT_CANONICAL, $e); 27945a874f4SNico $notSamePage = false; 28045a874f4SNico } 28145a874f4SNico if ($notSamePage) { 28245a874f4SNico $redirectionBuilder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_BEST_END_PAGE_NAME) 28345a874f4SNico ->setTargetMarkupPath($bestEndPage); 28445a874f4SNico switch ($method) { 28545a874f4SNico case RouterRedirection::REDIRECT_PERMANENT_METHOD: 28645a874f4SNico return $redirectionBuilder 28745a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 28845a874f4SNico ->build(); 28945a874f4SNico case RouterRedirection::REDIRECT_NOTFOUND_METHOD: 29045a874f4SNico return $redirectionBuilder 29145a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 29245a874f4SNico ->build(); 29345a874f4SNico default: 29445a874f4SNico LogUtility::error("This redirection method ($method) was not expected for the redirection algorithm ($algorithm)"); 29545a874f4SNico } 29645a874f4SNico } 29745a874f4SNico 29845a874f4SNico } 29945a874f4SNico break; 30045a874f4SNico 30145a874f4SNico case self::GO_TO_NS_START_PAGE: 30245a874f4SNico 30345a874f4SNico $redirectBuilder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_START_PAGE) 30445a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD); 30545a874f4SNico 30645a874f4SNico // Start page with the conf['start'] parameter 30745a874f4SNico $startPage = getNS($identifier) . ':' . $conf['start']; 30845a874f4SNico $startPath = MarkupPath::createMarkupFromId($startPage); 30945a874f4SNico if (FileSystems::exists($startPath)) { 31045a874f4SNico return $redirectBuilder->setTargetMarkupPath($startPath)->build(); 31145a874f4SNico } 31245a874f4SNico 31345a874f4SNico // Start page with the same name than the namespace 31445a874f4SNico $startPage = getNS($identifier) . ':' . curNS($identifier); 31545a874f4SNico $startPath = MarkupPath::createMarkupFromId($startPage); 31645a874f4SNico if (FileSystems::exists($startPath)) { 31745a874f4SNico return $redirectBuilder->setTargetMarkupPath($startPath)->build(); 31845a874f4SNico } 31945a874f4SNico 32045a874f4SNico break; 32145a874f4SNico 32245a874f4SNico case self::GO_TO_BEST_PAGE_NAME: 32345a874f4SNico 32445a874f4SNico $bestPageId = null; 32545a874f4SNico 32645a874f4SNico $bestPage = $this->getBestPage($identifier); 32745a874f4SNico $bestPageId = $bestPage['id']; 32845a874f4SNico $scorePageName = $bestPage['score']; 32945a874f4SNico 33045a874f4SNico // Get Score from a Namespace 33145a874f4SNico $bestNamespace = $this->scoreBestNamespace($identifier); 33245a874f4SNico $bestNamespaceId = $bestNamespace['namespace']; 33345a874f4SNico $namespaceScore = $bestNamespace['score']; 33445a874f4SNico 33545a874f4SNico // Compare the two score 33645a874f4SNico if ($scorePageName > 0 or $namespaceScore > 0) { 33745a874f4SNico $redirectionBuilder = RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_BEST_PAGE_NAME) 33845a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD); 33945a874f4SNico if ($scorePageName > $namespaceScore) { 34045a874f4SNico return $redirectionBuilder 34145a874f4SNico ->setTargetMarkupPath(MarkupPath::createMarkupFromId($bestPageId)) 34245a874f4SNico ->build(); 34345a874f4SNico } 34445a874f4SNico return $redirectionBuilder 34545a874f4SNico ->setTargetMarkupPath(MarkupPath::createMarkupFromId($bestNamespaceId)) 34645a874f4SNico ->build(); 34745a874f4SNico } 34845a874f4SNico break; 34945a874f4SNico 35045a874f4SNico case self::GO_TO_BEST_NAMESPACE: 35145a874f4SNico 35245a874f4SNico $scoreNamespace = $this->scoreBestNamespace($identifier); 35345a874f4SNico $bestNamespaceId = $scoreNamespace['namespace']; 35445a874f4SNico $score = $scoreNamespace['score']; 35545a874f4SNico 35645a874f4SNico if ($score > 0) { 35745a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_BEST_NAMESPACE) 35845a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 35945a874f4SNico ->setTargetMarkupPath(MarkupPath::createMarkupFromId($bestNamespaceId)) 36045a874f4SNico ->build(); 36145a874f4SNico } 36245a874f4SNico break; 36345a874f4SNico 36445a874f4SNico case self::GO_TO_SEARCH_ENGINE: 36545a874f4SNico 36645a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_SEARCH_ENGINE) 36745a874f4SNico ->setType(RouterRedirection::REDIRECT_NOTFOUND_METHOD) 36845a874f4SNico ->build(); 36945a874f4SNico 37045a874f4SNico } 37145a874f4SNico 37245a874f4SNico } 37345a874f4SNico 37445a874f4SNico throw new ExceptionNotFound(); 37545a874f4SNico 37645a874f4SNico } 37745a874f4SNico 37845a874f4SNico 37945a874f4SNico /** 38045a874f4SNico * @return string|null 38145a874f4SNico * 38245a874f4SNico * Return the original id from the request 38345a874f4SNico * ie `howto:how-to-get-started-with-combostrap-m3i8vga8` 38445a874f4SNico * if `/howto/how-to-get-started-with-combostrap-m3i8vga8` 38545a874f4SNico * 38645a874f4SNico * Unfortunately, DOKUWIKI_STARTED is not the first event 38745a874f4SNico * The id may have been changed by 38845a874f4SNico * {@link action_plugin_combo_lang::load_lang()} 38945a874f4SNico * function, that's why we have this function 39045a874f4SNico * to get the original requested id 39145a874f4SNico */ 39245a874f4SNico static function getOriginalIdFromRequest(): ?string 39345a874f4SNico { 39445a874f4SNico $originalId = $_GET["id"] ?? null; 39545a874f4SNico if ($originalId === null) { 39645a874f4SNico return null; 39745a874f4SNico } 39845a874f4SNico // We may get a `/` as first character 39945a874f4SNico // because we return an id, we need to delete it 40045a874f4SNico if (substr($originalId, 0, 1) === "/") { 40145a874f4SNico $originalId = substr($originalId, 1); 40245a874f4SNico } 40345a874f4SNico // transform / to : 40445a874f4SNico return str_replace("/", WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $originalId); 40545a874f4SNico } 40645a874f4SNico 40745a874f4SNico /** 40845a874f4SNico * Return a redirection declared in the redirection table or throw if not found 40945a874f4SNico * @throws ExceptionNotFound 41045a874f4SNico */ 41145a874f4SNico private function getRedirectionFromPageRules(): RouterRedirection 41245a874f4SNico { 41345a874f4SNico global $ID; 41445a874f4SNico 41545a874f4SNico $calculatedTarget = null; 41645a874f4SNico $ruleMatcher = null; // Used in a warning message if the target page does not exist 41745a874f4SNico // Known redirection in the table 41845a874f4SNico // Get the page from redirection data 41945a874f4SNico $rules = $this->pageRules->getRules(); 42045a874f4SNico foreach ($rules as $rule) { 42145a874f4SNico 42245a874f4SNico $ruleMatcher = strtolower($rule[PageRules::MATCHER_NAME]); 42345a874f4SNico $ruleTarget = $rule[PageRules::TARGET_NAME]; 42445a874f4SNico 42545a874f4SNico // Glob to Rexgexp 42645a874f4SNico $regexpPattern = '/' . str_replace("*", "(.*)", $ruleMatcher) . '/i'; 42745a874f4SNico 42845a874f4SNico // Match ? 42945a874f4SNico // https://www.php.net/manual/en/function.preg-match.php 43045a874f4SNico $pregMatchResult = @preg_match($regexpPattern, $ID, $matches); 43145a874f4SNico if ($pregMatchResult === false) { 43245a874f4SNico // The `if` to take into account this problem 43345a874f4SNico // PHP Warning: preg_match(): Unknown modifier 'd' in /opt/www/datacadamia.com/lib/plugins/combo/action/router.php on line 972 43445a874f4SNico LogUtility::log2file("processing Page Rules An error occurred with the pattern ($regexpPattern)", LogUtility::LVL_MSG_WARNING); 43545a874f4SNico throw new ExceptionNotFound(); 43645a874f4SNico } 43745a874f4SNico if ($pregMatchResult) { 43845a874f4SNico $calculatedTarget = $ruleTarget; 43945a874f4SNico foreach ($matches as $key => $match) { 44045a874f4SNico if ($key == 0) { 44145a874f4SNico continue; 44245a874f4SNico } else { 44345a874f4SNico $calculatedTarget = str_replace('$' . $key, $match, $calculatedTarget); 44445a874f4SNico } 44545a874f4SNico } 44645a874f4SNico break; 44745a874f4SNico } 44845a874f4SNico } 44945a874f4SNico 45045a874f4SNico if ($calculatedTarget == null) { 45145a874f4SNico throw new ExceptionNotFound(); 45245a874f4SNico } 45345a874f4SNico 45445a874f4SNico // If this is an external redirect (other domain) 45545a874f4SNico try { 45645a874f4SNico $url = Url::createFromString($calculatedTarget); 457*313de40aSNicolas GERARD // The page id `my:page` is a valid url after parsing with the scheme `my` 458*313de40aSNicolas GERARD if (strpos($url->getScheme(), "http") === 0) { 45945a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PAGE_RULES) 46045a874f4SNico ->setTargetUrl($url) 46145a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 46245a874f4SNico ->build(); 463*313de40aSNicolas GERARD } 46445a874f4SNico } catch (ExceptionBadSyntax|ExceptionBadArgument $e) { 46545a874f4SNico // not an URL 46645a874f4SNico } 46745a874f4SNico 46845a874f4SNico 46945a874f4SNico // If the page exist 47045a874f4SNico // This is DokuWiki Id and should always be lowercase 47145a874f4SNico // The page rule may have change that 47245a874f4SNico $calculatedTarget = strtolower($calculatedTarget); 47345a874f4SNico $markupPath = MarkupPath::createMarkupFromId($calculatedTarget); 47445a874f4SNico if (FileSystems::exists($markupPath)) { 47545a874f4SNico 47645a874f4SNico return RouterRedirectionBuilder::createFromOrigin(RouterRedirection::TARGET_ORIGIN_PAGE_RULES) 47745a874f4SNico ->setTargetMarkupPath($markupPath) 47845a874f4SNico ->setType(RouterRedirection::REDIRECT_PERMANENT_METHOD) 47945a874f4SNico ->build(); 48045a874f4SNico 48145a874f4SNico } 48245a874f4SNico 48345a874f4SNico LogUtility::error("The calculated target page ($calculatedTarget) (for the non-existing page `$ID` with the matcher `$ruleMatcher`) does not exist"); 48445a874f4SNico throw new ExceptionNotFound(); 48545a874f4SNico 48645a874f4SNico } 48745a874f4SNico 48845a874f4SNico 48945a874f4SNico /** 49045a874f4SNico * @param $id 49145a874f4SNico * @return array 49245a874f4SNico */ 49345a874f4SNico private 49445a874f4SNico function getBestPage($id): array 49545a874f4SNico { 49645a874f4SNico 49745a874f4SNico // The return parameters 49845a874f4SNico $bestPageId = null; 49945a874f4SNico $scorePageName = null; 50045a874f4SNico 50145a874f4SNico // Get Score from a page 50245a874f4SNico $pageName = noNS($id); 50345a874f4SNico $pagesWithSameName = ft_pageLookup($pageName); 50445a874f4SNico if (count($pagesWithSameName) > 0) { 50545a874f4SNico 50645a874f4SNico // Search same namespace in the page found than in the Id page asked. 50745a874f4SNico $bestNbWordFound = 0; 50845a874f4SNico 50945a874f4SNico 51045a874f4SNico $wordsInPageSourceId = explode(':', $id); 51145a874f4SNico foreach ($pagesWithSameName as $targetPageId => $title) { 51245a874f4SNico 51345a874f4SNico // Nb of word found in the target page id 51445a874f4SNico // that are in the source page id 51545a874f4SNico $nbWordFound = 0; 51645a874f4SNico foreach ($wordsInPageSourceId as $word) { 51745a874f4SNico $nbWordFound = $nbWordFound + substr_count($targetPageId, $word); 51845a874f4SNico } 51945a874f4SNico 52045a874f4SNico if ($bestPageId == null) { 52145a874f4SNico 52245a874f4SNico $bestNbWordFound = $nbWordFound; 52345a874f4SNico $bestPageId = $targetPageId; 52445a874f4SNico 52545a874f4SNico } else { 52645a874f4SNico 52745a874f4SNico if ($nbWordFound >= $bestNbWordFound && strlen($bestPageId) > strlen($targetPageId)) { 52845a874f4SNico 52945a874f4SNico $bestNbWordFound = $nbWordFound; 53045a874f4SNico $bestPageId = $targetPageId; 53145a874f4SNico 53245a874f4SNico } 53345a874f4SNico 53445a874f4SNico } 53545a874f4SNico 53645a874f4SNico } 53745a874f4SNico $config = ExecutionContext::getActualOrCreateFromEnv()->getConfig(); 53845a874f4SNico $weightFactorForSamePageName = $config->getValue('WeightFactorForSamePageName'); 53945a874f4SNico $weightFactorForSameNamespace = $config->getValue('WeightFactorForSameNamespace'); 54045a874f4SNico $scorePageName = $weightFactorForSamePageName + ($bestNbWordFound - 1) * $weightFactorForSameNamespace; 54145a874f4SNico return array( 54245a874f4SNico 'id' => $bestPageId, 54345a874f4SNico 'score' => $scorePageName); 54445a874f4SNico } 54545a874f4SNico return array( 54645a874f4SNico 'id' => $bestPageId, 54745a874f4SNico 'score' => $scorePageName 54845a874f4SNico ); 54945a874f4SNico 55045a874f4SNico } 55145a874f4SNico 55245a874f4SNico /** 55345a874f4SNico * getBestNamespace 55445a874f4SNico * Return a list with 'BestNamespaceId Score' 55545a874f4SNico * @param $id 55645a874f4SNico * @return array 55745a874f4SNico */ 55845a874f4SNico private 55945a874f4SNico function scoreBestNamespace($id): array 56045a874f4SNico { 56145a874f4SNico 56245a874f4SNico $nameSpaces = array(); 56345a874f4SNico $pathNames = array(); 56445a874f4SNico 56545a874f4SNico // Parameters 56645a874f4SNico $requestedPath = MarkupPath::createMarkupFromId($id); 56745a874f4SNico try { 56845a874f4SNico $pageNameSpace = $requestedPath->getParent(); 56945a874f4SNico $pathNames = array_slice($pageNameSpace->getNames(), 0, -1); 57045a874f4SNico if (FileSystems::exists($pageNameSpace)) { 57145a874f4SNico $nameSpaces = array($pageNameSpace->toAbsoluteId()); 57245a874f4SNico } else { 57345a874f4SNico global $conf; 57445a874f4SNico $nameSpaces = ft_pageLookup($conf['start']); 57545a874f4SNico } 57645a874f4SNico } catch (ExceptionNotFound $e) { 57745a874f4SNico // no parent, root 57845a874f4SNico } 57945a874f4SNico 58045a874f4SNico // Parameters and search the best namespace 58145a874f4SNico $bestNbWordFound = 0; 58245a874f4SNico $bestNamespaceId = null; 58345a874f4SNico foreach ($nameSpaces as $nameSpace) { 58445a874f4SNico 58545a874f4SNico $nbWordFound = 0; 58645a874f4SNico foreach ($pathNames as $pathName) { 58745a874f4SNico if (strlen($pathName) > 2) { 58845a874f4SNico $nbWordFound = $nbWordFound + substr_count($nameSpace, $pathName); 58945a874f4SNico } 59045a874f4SNico } 59145a874f4SNico if ($nbWordFound > $bestNbWordFound) { 59245a874f4SNico // Take only the smallest namespace 59345a874f4SNico if ($bestNbWordFound == null || strlen($nameSpace) < strlen($bestNamespaceId)) { 59445a874f4SNico $bestNbWordFound = $nbWordFound; 59545a874f4SNico $bestNamespaceId = $nameSpace; 59645a874f4SNico } 59745a874f4SNico } 59845a874f4SNico } 59945a874f4SNico $config = ExecutionContext::getActualOrCreateFromEnv()->getConfig(); 60045a874f4SNico $startPageFactor = $config->getValue('WeightFactorForStartPage'); 60145a874f4SNico $nameSpaceFactor = $config->getValue('WeightFactorForSameNamespace'); 60245a874f4SNico if ($bestNbWordFound > 0) { 60345a874f4SNico $bestNamespaceScore = $bestNbWordFound * $nameSpaceFactor + $startPageFactor; 60445a874f4SNico } else { 60545a874f4SNico $bestNamespaceScore = 0; 60645a874f4SNico } 60745a874f4SNico 60845a874f4SNico 60945a874f4SNico return array( 61045a874f4SNico 'namespace' => $bestNamespaceId, 61145a874f4SNico 'score' => $bestNamespaceScore 61245a874f4SNico ); 61345a874f4SNico 61445a874f4SNico } 61545a874f4SNico 61645a874f4SNico 61745a874f4SNico} 618