xref: /plugin/combo/ComboStrap/FetcherPage.php (revision 49b8fb2440a8b019e2a082c98b66ddbf7fc707db)
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