104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeaunamespace ComboStrap; 404fd306cSNickeau 504fd306cSNickeau 604fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName; 704fd306cSNickeauuse ComboStrap\Web\Url; 804fd306cSNickeauuse ComboStrap\Web\UrlEndpoint; 965e00826Sgerardnicouse ComboStrap\Web\UrlRewrite; 1004fd306cSNickeauuse ComboStrap\Xml\XmlDocument; 1104fd306cSNickeau 1204fd306cSNickeauclass FetcherPage extends IFetcherAbs implements IFetcherSource, IFetcherString 1304fd306cSNickeau{ 1404fd306cSNickeau 1504fd306cSNickeau use FetcherTraitWikiPath; 1604fd306cSNickeau 1704fd306cSNickeau const NAME = "page"; 1804fd306cSNickeau const CANONICAL = "page"; 1904fd306cSNickeau const PURGE = "purge"; 2004fd306cSNickeau 2104fd306cSNickeau private string $requestedLayout; 2204fd306cSNickeau private bool $build = false; 2304fd306cSNickeau private bool $closed = false; 2404fd306cSNickeau 2504fd306cSNickeau 2604fd306cSNickeau private MarkupPath $requestedMarkupPath; 2704fd306cSNickeau private string $requestedLayoutName; 2804fd306cSNickeau private TemplateForWebPage $pageTemplate; 2904fd306cSNickeau private FetcherCache $fetcherCache; 3004fd306cSNickeau 3104fd306cSNickeau 3204fd306cSNickeau public static function createPageFetcherFromPath(Path $path): FetcherPage 3304fd306cSNickeau { 3404fd306cSNickeau $fetcherPage = new FetcherPage(); 3504fd306cSNickeau $fetcherPage->setRequestedPath($path); 3604fd306cSNickeau return $fetcherPage; 3704fd306cSNickeau } 3804fd306cSNickeau 3904fd306cSNickeau public static function createPageFetcherFromId(string $wikiId): FetcherPage 4004fd306cSNickeau { 4104fd306cSNickeau $wikiPath = WikiPath::createMarkupPathFromId($wikiId); 4204fd306cSNickeau return self::createPageFetcherFromPath($wikiPath); 4304fd306cSNickeau } 4404fd306cSNickeau 4504fd306cSNickeau /** 4604fd306cSNickeau * @throws ExceptionBadArgument 4704fd306cSNickeau */ 4804fd306cSNickeau public static function createPageFragmentFetcherFromUrl(Url $fetchUrl): FetcherPage 4904fd306cSNickeau { 5004fd306cSNickeau $pageFragment = new FetcherPage(); 5165e00826Sgerardnico $rewrite = Site::getUrlRewrite(); 5265e00826Sgerardnico if ($rewrite === UrlRewrite::WEB_SERVER_REWRITE) { 5365e00826Sgerardnico try { 5465e00826Sgerardnico $path = $fetchUrl->getPath(); 5565e00826Sgerardnico $id = substr(str_replace("/", ":", $path), 1); 5665e00826Sgerardnico $fetchUrl->setPath(UrlEndpoint::DOKU_PHP); 5765e00826Sgerardnico $fetchUrl->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $id); 5865e00826Sgerardnico } catch (ExceptionNotFound $e) { 5965e00826Sgerardnico // Paht should be there 6065e00826Sgerardnico } 6165e00826Sgerardnico } 6204fd306cSNickeau $pageFragment->buildFromUrl($fetchUrl); 6304fd306cSNickeau return $pageFragment; 6404fd306cSNickeau } 6504fd306cSNickeau 6604fd306cSNickeau /** 6704fd306cSNickeau * @param Url|null $url 6804fd306cSNickeau * @return Url 6904fd306cSNickeau * 7004fd306cSNickeau * Note: The fetch url is the {@link FetcherCache keyCache} 7104fd306cSNickeau */ 7204fd306cSNickeau function getFetchUrl(Url $url = null): Url 7304fd306cSNickeau { 7404fd306cSNickeau /** 7504fd306cSNickeau * Overwrite default fetcher endpoint 7604fd306cSNickeau * that is {@link UrlEndpoint::createFetchUrl()} 7704fd306cSNickeau */ 7804fd306cSNickeau $url = UrlEndpoint::createDokuUrl(); 7904fd306cSNickeau $url = parent::getFetchUrl($url); 8004fd306cSNickeau try { 8104fd306cSNickeau $template = $this->getRequestedTemplate(); 8204fd306cSNickeau $url->addQueryParameter(PageTemplateName::PROPERTY_NAME, $template); 8304fd306cSNickeau } catch (ExceptionNotFound $e) { 8404fd306cSNickeau // ok 8504fd306cSNickeau } 8604fd306cSNickeau 8704fd306cSNickeau $this->addLocalPathParametersToFetchUrl($url, DokuwikiId::DOKUWIKI_ID_ATTRIBUTE); 8804fd306cSNickeau 8904fd306cSNickeau // the drive is not needed 9004fd306cSNickeau $url->deleteQueryParameter(WikiPath::DRIVE_ATTRIBUTE); 9104fd306cSNickeau 9204fd306cSNickeau // this is the default fetcher, no need to add it as parameter 9304fd306cSNickeau $url->deleteQueryParameter(IFetcher::FETCHER_KEY); 9404fd306cSNickeau 9504fd306cSNickeau return $url; 9604fd306cSNickeau } 9704fd306cSNickeau 9804fd306cSNickeau /** 9904fd306cSNickeau * @throws ExceptionBadArgument 10004fd306cSNickeau * @throws ExceptionBadSyntax 10104fd306cSNickeau * @throws ExceptionNotExists 10204fd306cSNickeau * @throws ExceptionNotFound 10304fd306cSNickeau */ 10404fd306cSNickeau public function buildFromTagAttributes(TagAttributes $tagAttributes): IFetcher 10504fd306cSNickeau { 10604fd306cSNickeau parent::buildFromTagAttributes($tagAttributes); 10704fd306cSNickeau $this->buildOriginalPathFromTagAttributes($tagAttributes); 10804fd306cSNickeau $layout = $tagAttributes->getValueAndRemoveIfPresent(PageTemplateName::PROPERTY_NAME); 10904fd306cSNickeau if ($layout !== null) { 11004fd306cSNickeau $this->setRequestedLayout($layout); 11104fd306cSNickeau } 11204fd306cSNickeau /** 11304fd306cSNickeau * Purge the cache 11404fd306cSNickeau */ 11504fd306cSNickeau $tagAttributes->getValueAndRemoveIfPresent(self::PURGE); 11604fd306cSNickeau return $this; 11704fd306cSNickeau } 11804fd306cSNickeau 11904fd306cSNickeau 12004fd306cSNickeau public static function createPageFetcherFromMarkupPath(MarkupPath $markupPath): FetcherPage 12104fd306cSNickeau { 12204fd306cSNickeau return self::createPageFetcherFromPath($markupPath->getPathObject()); 12304fd306cSNickeau } 12404fd306cSNickeau 12504fd306cSNickeau 12604fd306cSNickeau /** 12704fd306cSNickeau * @return string 12804fd306cSNickeau */ 12904fd306cSNickeau public function getFetchString(): string 13004fd306cSNickeau { 13104fd306cSNickeau 13204fd306cSNickeau $this->buildObjectIfNeeded(); 13304fd306cSNickeau 13404fd306cSNickeau $cache = $this->fetcherCache; 13504fd306cSNickeau 13604fd306cSNickeau /** 13704fd306cSNickeau * Public static cache 13804fd306cSNickeau * (Do we create the page or return the cache) 13904fd306cSNickeau */ 14004fd306cSNickeau $isPublicStaticPage = $this->isPublicStaticPage(); 14104fd306cSNickeau $isCacheUsable = $cache->isCacheUsable(); 14204fd306cSNickeau if ($isCacheUsable && $isPublicStaticPage) { 14304fd306cSNickeau try { 14404fd306cSNickeau return FileSystems::getContent($cache->getFile()); 14504fd306cSNickeau } catch (ExceptionNotFound $e) { 14604fd306cSNickeau // the cache file should exists 14704fd306cSNickeau LogUtility::internalError("The cache HTML fragment file was not found", self::NAME); 14804fd306cSNickeau } 14904fd306cSNickeau } 15004fd306cSNickeau 15104fd306cSNickeau /** 15204fd306cSNickeau * Generate the whole html page via the layout 15304fd306cSNickeau */ 15404fd306cSNickeau $htmlDocumentString = $this->pageTemplate->render(); 15504fd306cSNickeau 15670bbd7f1Sgerardnico 15704fd306cSNickeau /** 15804fd306cSNickeau * We store only the static public pages 15904fd306cSNickeau * without messages (they are dynamically insert) 16004fd306cSNickeau */ 16104fd306cSNickeau $hasMessages = $this->pageTemplate->hasMessages(); 16204fd306cSNickeau if ($isPublicStaticPage && !$hasMessages) { 16304fd306cSNickeau $cache->storeCache($htmlDocumentString); 16404fd306cSNickeau } 16504fd306cSNickeau 16604fd306cSNickeau return $htmlDocumentString; 16704fd306cSNickeau 16804fd306cSNickeau } 16904fd306cSNickeau 17004fd306cSNickeau 17104fd306cSNickeau /** 17204fd306cSNickeau * 17304fd306cSNickeau */ 17404fd306cSNickeau function getBuster(): string 17504fd306cSNickeau { 17604fd306cSNickeau return ""; 17704fd306cSNickeau } 17804fd306cSNickeau 17904fd306cSNickeau public function getMime(): Mime 18004fd306cSNickeau { 18104fd306cSNickeau return Mime::create(Mime::HTML); 18204fd306cSNickeau } 18304fd306cSNickeau 18404fd306cSNickeau public function getFetcherName(): string 18504fd306cSNickeau { 18604fd306cSNickeau return self::NAME; 18704fd306cSNickeau } 18804fd306cSNickeau 18904fd306cSNickeau /** 19004fd306cSNickeau * @throws ExceptionBadSyntax 19104fd306cSNickeau * @throws ExceptionNotFound 19204fd306cSNickeau * @throws ExceptionBadArgument 19304fd306cSNickeau */ 19404fd306cSNickeau public function getFetchAsHtmlDom(): XmlDocument 19504fd306cSNickeau { 19604fd306cSNickeau $content = $this->getFetchString(); 19704fd306cSNickeau return XmlDocument::createHtmlDocFromMarkup($content); 19804fd306cSNickeau } 19904fd306cSNickeau 20004fd306cSNickeau 20104fd306cSNickeau /** 20204fd306cSNickeau * 20304fd306cSNickeau */ 20404fd306cSNickeau private function buildObjectIfNeeded(): void 20504fd306cSNickeau { 20604fd306cSNickeau 20704fd306cSNickeau if ($this->build) { 20804fd306cSNickeau if ($this->closed) { 20904fd306cSNickeau throw new ExceptionRuntimeInternal("This fetcher page object has already been close and cannot be reused", self::NAME); 21004fd306cSNickeau } 21104fd306cSNickeau return; 21204fd306cSNickeau } 21304fd306cSNickeau 21404fd306cSNickeau $this->build = true; 21504fd306cSNickeau 21604fd306cSNickeau $this->requestedMarkupPath = MarkupPath::createPageFromPathObject($this->getRequestedPath()); 21704fd306cSNickeau 21804fd306cSNickeau $pageLang = Lang::createForMarkup($this->getRequestedPage()); 21904fd306cSNickeau $title = PageTitle::createForMarkup($this->getRequestedPage())->getValueOrDefault(); 22004fd306cSNickeau 22104fd306cSNickeau $layoutName = $this->getRequestedTemplateOrDefault(); 22204fd306cSNickeau $this->pageTemplate = TemplateForWebPage::create() 22304fd306cSNickeau ->setRequestedTemplateName($layoutName) 22404fd306cSNickeau ->setRequestedContextPath($this->getRequestedPath()) 22504fd306cSNickeau ->setRequestedLang($pageLang) 22604fd306cSNickeau ->setRequestedTitle($title); 22704fd306cSNickeau 22804fd306cSNickeau /** 22904fd306cSNickeau * Build the cache 23004fd306cSNickeau * The template is mandatory as cache key 23104fd306cSNickeau * If the user change it, the cache should be disabled 23204fd306cSNickeau */ 23304fd306cSNickeau $this->fetcherCache = FetcherCache::createFrom($this, [$layoutName]); 23404fd306cSNickeau // the requested page 23504fd306cSNickeau $this->fetcherCache->addFileDependency($this->getRequestedPath()); 23604fd306cSNickeau if (PluginUtility::isDevOrTest()) { 23704fd306cSNickeau /** 23804fd306cSNickeau * The hbs template dependency 23904fd306cSNickeau * (only on test/dev) 24004fd306cSNickeau */ 24104fd306cSNickeau try { 24204fd306cSNickeau $this->fetcherCache->addFileDependency($this->pageTemplate->getCssPath()); 24304fd306cSNickeau } catch (ExceptionNotFound $e) { 24404fd306cSNickeau // no css file 24504fd306cSNickeau } 24604fd306cSNickeau try { 24704fd306cSNickeau $this->fetcherCache->addFileDependency($this->pageTemplate->getJsPath()); 24804fd306cSNickeau } catch (ExceptionNotFound $e) { 24904fd306cSNickeau // no js 25004fd306cSNickeau } 25104fd306cSNickeau // mandatory, should not throw 25204fd306cSNickeau try { 25304fd306cSNickeau $this->fetcherCache->addFileDependency($this->pageTemplate->getHtmlTemplatePath()); 25404fd306cSNickeau } catch (ExceptionNotFound $e) { 25504fd306cSNickeau LogUtility::internalError("The html template should be found", self::CANONICAL, $e); 25604fd306cSNickeau } 25704fd306cSNickeau } 25804fd306cSNickeau /** 25904fd306cSNickeau * The Slots of the requested template 26004fd306cSNickeau */ 26104fd306cSNickeau foreach ($this->pageTemplate->getSlots() as $templateSlot) { 26204fd306cSNickeau try { 26304fd306cSNickeau $this->fetcherCache->addFileDependency($templateSlot->getMarkupFetcher()->getSourcePath()); 26404fd306cSNickeau } catch (ExceptionNotFound $e) { 26504fd306cSNickeau // no slot page found 26604fd306cSNickeau } 26704fd306cSNickeau } 26804fd306cSNickeau 26904fd306cSNickeau } 27004fd306cSNickeau 27104fd306cSNickeau public function getRequestedPath(): WikiPath 27204fd306cSNickeau { 27304fd306cSNickeau return $this->getSourcePath(); 27404fd306cSNickeau } 27504fd306cSNickeau 27604fd306cSNickeau public function setRequestedLayout(string $layoutValue): FetcherPage 27704fd306cSNickeau { 27804fd306cSNickeau $this->requestedLayout = $layoutValue; 27904fd306cSNickeau return $this; 28004fd306cSNickeau } 28104fd306cSNickeau 28204fd306cSNickeau /** 28304fd306cSNickeau * @throws ExceptionNotFound 28404fd306cSNickeau */ 28504fd306cSNickeau private function getRequestedTemplate(): string 28604fd306cSNickeau { 28704fd306cSNickeau if (!isset($this->requestedLayout)) { 28804fd306cSNickeau throw new ExceptionNotFound("No requested layout"); 28904fd306cSNickeau } 29004fd306cSNickeau return $this->requestedLayout; 29104fd306cSNickeau } 29204fd306cSNickeau 29304fd306cSNickeau 29404fd306cSNickeau public function setRequestedPath(Path $requestedPath): FetcherPage 29504fd306cSNickeau { 29604fd306cSNickeau try { 29704fd306cSNickeau $requestedPath = WikiPath::createFromPathObject($requestedPath); 29804fd306cSNickeau } catch (ExceptionBadArgument $e) { 29904fd306cSNickeau throw new ExceptionRuntimeInternal("Not a local wiki path", self::NAME, 1, $e); 30004fd306cSNickeau } 30104fd306cSNickeau $this->setSourcePath($requestedPath); 30204fd306cSNickeau return $this; 30304fd306cSNickeau } 30404fd306cSNickeau 30504fd306cSNickeau 30604fd306cSNickeau /** 30704fd306cSNickeau * 30804fd306cSNickeau */ 30904fd306cSNickeau public function close(): FetcherPage 31004fd306cSNickeau { 31104fd306cSNickeau // nothing to do 31204fd306cSNickeau return $this; 31304fd306cSNickeau } 31404fd306cSNickeau 31504fd306cSNickeau 31604fd306cSNickeau private function getRequestedPage(): MarkupPath 31704fd306cSNickeau { 31804fd306cSNickeau $this->buildObjectIfNeeded(); 31904fd306cSNickeau return $this->requestedMarkupPath; 32004fd306cSNickeau } 32104fd306cSNickeau 32204fd306cSNickeau 32304fd306cSNickeau /** 32404fd306cSNickeau * The cache stores only public pages. 32504fd306cSNickeau * 32604fd306cSNickeau * ie when the user is unknown 32704fd306cSNickeau * and there is no railbar 32804fd306cSNickeau * (railbar is dynamically created even for the public 32904fd306cSNickeau * and the javascript for the menu item expects to run after a window load event) 33004fd306cSNickeau * 33104fd306cSNickeau * @return bool 33204fd306cSNickeau */ 33304fd306cSNickeau private function isPublicStaticPage(): bool 33404fd306cSNickeau { 335*49b8fb24Sgerardnico $privateRailbar = SiteConfig::getConfValue(FetcherRailBar::CONF_PRIVATE_RAIL_BAR, FetcherRailBar::CONF_PRIVATE_RAIL_BAR_DEFAULT); 33604fd306cSNickeau $isLoggedIn = Identity::isLoggedIn(); 33704fd306cSNickeau return $privateRailbar === 1 && !$isLoggedIn; 33804fd306cSNickeau } 33904fd306cSNickeau 34004fd306cSNickeau private function getRequestedTemplateOrDefault(): string 34104fd306cSNickeau { 34204fd306cSNickeau try { 34304fd306cSNickeau return $this->getRequestedTemplate(); 34404fd306cSNickeau } catch (ExceptionNotFound $e) { 34504fd306cSNickeau return PageTemplateName::createFromPage($this->getRequestedPage())->getValueOrDefault(); 34604fd306cSNickeau } 34704fd306cSNickeau } 34804fd306cSNickeau 34904fd306cSNickeau public function getContentCachePath(): LocalPath 35004fd306cSNickeau { 35104fd306cSNickeau $this->buildObjectIfNeeded(); 35204fd306cSNickeau return $this->fetcherCache->getFile(); 35304fd306cSNickeau } 35404fd306cSNickeau 35504fd306cSNickeau public function getLabel(): string 35604fd306cSNickeau { 35704fd306cSNickeau $sourcePath = $this->getSourcePath(); 35804fd306cSNickeau return ResourceName::getFromPath($sourcePath); 35904fd306cSNickeau } 36004fd306cSNickeau 36104fd306cSNickeau 36204fd306cSNickeau} 363