xref: /plugin/combo/ComboStrap/FetcherPage.php (revision 65e00826e4eceefc13be6f404ba5dcc7c44f243c)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeaunamespace ComboStrap;
404fd306cSNickeau
504fd306cSNickeau
604fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName;
704fd306cSNickeauuse ComboStrap\Web\Url;
804fd306cSNickeauuse ComboStrap\Web\UrlEndpoint;
9*65e00826Sgerardnicouse 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();
51*65e00826Sgerardnico        $rewrite = Site::getUrlRewrite();
52*65e00826Sgerardnico        if ($rewrite === UrlRewrite::WEB_SERVER_REWRITE) {
53*65e00826Sgerardnico            try {
54*65e00826Sgerardnico                $path = $fetchUrl->getPath();
55*65e00826Sgerardnico                $id = substr(str_replace("/", ":", $path), 1);
56*65e00826Sgerardnico                $fetchUrl->setPath(UrlEndpoint::DOKU_PHP);
57*65e00826Sgerardnico                $fetchUrl->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $id);
58*65e00826Sgerardnico            } catch (ExceptionNotFound $e) {
59*65e00826Sgerardnico                // Paht should be there
60*65e00826Sgerardnico            }
61*65e00826Sgerardnico        }
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
15604fd306cSNickeau        /**
15704fd306cSNickeau         * We store only the static public pages
15804fd306cSNickeau         * without messages (they are dynamically insert)
15904fd306cSNickeau         */
16004fd306cSNickeau        $hasMessages = $this->pageTemplate->hasMessages();
16104fd306cSNickeau        if ($isPublicStaticPage && !$hasMessages) {
16204fd306cSNickeau            $cache->storeCache($htmlDocumentString);
16304fd306cSNickeau        }
16404fd306cSNickeau
16504fd306cSNickeau        return $htmlDocumentString;
16604fd306cSNickeau
16704fd306cSNickeau    }
16804fd306cSNickeau
16904fd306cSNickeau
17004fd306cSNickeau    /**
17104fd306cSNickeau     *
17204fd306cSNickeau     */
17304fd306cSNickeau    function getBuster(): string
17404fd306cSNickeau    {
17504fd306cSNickeau        return "";
17604fd306cSNickeau    }
17704fd306cSNickeau
17804fd306cSNickeau    public function getMime(): Mime
17904fd306cSNickeau    {
18004fd306cSNickeau        return Mime::create(Mime::HTML);
18104fd306cSNickeau    }
18204fd306cSNickeau
18304fd306cSNickeau    public function getFetcherName(): string
18404fd306cSNickeau    {
18504fd306cSNickeau        return self::NAME;
18604fd306cSNickeau    }
18704fd306cSNickeau
18804fd306cSNickeau    /**
18904fd306cSNickeau     * @throws ExceptionBadSyntax
19004fd306cSNickeau     * @throws ExceptionNotFound
19104fd306cSNickeau     * @throws ExceptionBadArgument
19204fd306cSNickeau     */
19304fd306cSNickeau    public function getFetchAsHtmlDom(): XmlDocument
19404fd306cSNickeau    {
19504fd306cSNickeau        $content = $this->getFetchString();
19604fd306cSNickeau        return XmlDocument::createHtmlDocFromMarkup($content);
19704fd306cSNickeau    }
19804fd306cSNickeau
19904fd306cSNickeau
20004fd306cSNickeau    /**
20104fd306cSNickeau     *
20204fd306cSNickeau     */
20304fd306cSNickeau    private function buildObjectIfNeeded(): void
20404fd306cSNickeau    {
20504fd306cSNickeau
20604fd306cSNickeau        if ($this->build) {
20704fd306cSNickeau            if ($this->closed) {
20804fd306cSNickeau                throw new ExceptionRuntimeInternal("This fetcher page object has already been close and cannot be reused", self::NAME);
20904fd306cSNickeau            }
21004fd306cSNickeau            return;
21104fd306cSNickeau        }
21204fd306cSNickeau
21304fd306cSNickeau        $this->build = true;
21404fd306cSNickeau
21504fd306cSNickeau        $this->requestedMarkupPath = MarkupPath::createPageFromPathObject($this->getRequestedPath());
21604fd306cSNickeau
21704fd306cSNickeau        $pageLang = Lang::createForMarkup($this->getRequestedPage());
21804fd306cSNickeau        $title = PageTitle::createForMarkup($this->getRequestedPage())->getValueOrDefault();
21904fd306cSNickeau
22004fd306cSNickeau        $layoutName = $this->getRequestedTemplateOrDefault();
22104fd306cSNickeau        $this->pageTemplate = TemplateForWebPage::create()
22204fd306cSNickeau            ->setRequestedTemplateName($layoutName)
22304fd306cSNickeau            ->setRequestedContextPath($this->getRequestedPath())
22404fd306cSNickeau            ->setRequestedLang($pageLang)
22504fd306cSNickeau            ->setRequestedTitle($title);
22604fd306cSNickeau
22704fd306cSNickeau        /**
22804fd306cSNickeau         * Build the cache
22904fd306cSNickeau         * The template is mandatory as cache key
23004fd306cSNickeau         * If the user change it, the cache should be disabled
23104fd306cSNickeau         */
23204fd306cSNickeau        $this->fetcherCache = FetcherCache::createFrom($this, [$layoutName]);
23304fd306cSNickeau        // the requested page
23404fd306cSNickeau        $this->fetcherCache->addFileDependency($this->getRequestedPath());
23504fd306cSNickeau        if (PluginUtility::isDevOrTest()) {
23604fd306cSNickeau            /**
23704fd306cSNickeau             * The hbs template dependency
23804fd306cSNickeau             * (only on test/dev)
23904fd306cSNickeau             */
24004fd306cSNickeau            try {
24104fd306cSNickeau                $this->fetcherCache->addFileDependency($this->pageTemplate->getCssPath());
24204fd306cSNickeau            } catch (ExceptionNotFound $e) {
24304fd306cSNickeau                // no css file
24404fd306cSNickeau            }
24504fd306cSNickeau            try {
24604fd306cSNickeau                $this->fetcherCache->addFileDependency($this->pageTemplate->getJsPath());
24704fd306cSNickeau            } catch (ExceptionNotFound $e) {
24804fd306cSNickeau                // no js
24904fd306cSNickeau            }
25004fd306cSNickeau            // mandatory, should not throw
25104fd306cSNickeau            try {
25204fd306cSNickeau                $this->fetcherCache->addFileDependency($this->pageTemplate->getHtmlTemplatePath());
25304fd306cSNickeau            } catch (ExceptionNotFound $e) {
25404fd306cSNickeau                LogUtility::internalError("The html template should be found", self::CANONICAL, $e);
25504fd306cSNickeau            }
25604fd306cSNickeau        }
25704fd306cSNickeau        /**
25804fd306cSNickeau         * The Slots of the requested template
25904fd306cSNickeau         */
26004fd306cSNickeau        foreach ($this->pageTemplate->getSlots() as $templateSlot) {
26104fd306cSNickeau            try {
26204fd306cSNickeau                $this->fetcherCache->addFileDependency($templateSlot->getMarkupFetcher()->getSourcePath());
26304fd306cSNickeau            } catch (ExceptionNotFound $e) {
26404fd306cSNickeau                // no slot page found
26504fd306cSNickeau            }
26604fd306cSNickeau        }
26704fd306cSNickeau
26804fd306cSNickeau    }
26904fd306cSNickeau
27004fd306cSNickeau    public function getRequestedPath(): WikiPath
27104fd306cSNickeau    {
27204fd306cSNickeau        return $this->getSourcePath();
27304fd306cSNickeau    }
27404fd306cSNickeau
27504fd306cSNickeau    public function setRequestedLayout(string $layoutValue): FetcherPage
27604fd306cSNickeau    {
27704fd306cSNickeau        $this->requestedLayout = $layoutValue;
27804fd306cSNickeau        return $this;
27904fd306cSNickeau    }
28004fd306cSNickeau
28104fd306cSNickeau    /**
28204fd306cSNickeau     * @throws ExceptionNotFound
28304fd306cSNickeau     */
28404fd306cSNickeau    private function getRequestedTemplate(): string
28504fd306cSNickeau    {
28604fd306cSNickeau        if (!isset($this->requestedLayout)) {
28704fd306cSNickeau            throw new ExceptionNotFound("No requested layout");
28804fd306cSNickeau        }
28904fd306cSNickeau        return $this->requestedLayout;
29004fd306cSNickeau    }
29104fd306cSNickeau
29204fd306cSNickeau
29304fd306cSNickeau    public function setRequestedPath(Path $requestedPath): FetcherPage
29404fd306cSNickeau    {
29504fd306cSNickeau        try {
29604fd306cSNickeau            $requestedPath = WikiPath::createFromPathObject($requestedPath);
29704fd306cSNickeau        } catch (ExceptionBadArgument $e) {
29804fd306cSNickeau            throw new ExceptionRuntimeInternal("Not a local wiki path", self::NAME, 1, $e);
29904fd306cSNickeau        }
30004fd306cSNickeau        $this->setSourcePath($requestedPath);
30104fd306cSNickeau        return $this;
30204fd306cSNickeau    }
30304fd306cSNickeau
30404fd306cSNickeau
30504fd306cSNickeau    /**
30604fd306cSNickeau     *
30704fd306cSNickeau     */
30804fd306cSNickeau    public function close(): FetcherPage
30904fd306cSNickeau    {
31004fd306cSNickeau        // nothing to do
31104fd306cSNickeau        return $this;
31204fd306cSNickeau    }
31304fd306cSNickeau
31404fd306cSNickeau
31504fd306cSNickeau    private function getRequestedPage(): MarkupPath
31604fd306cSNickeau    {
31704fd306cSNickeau        $this->buildObjectIfNeeded();
31804fd306cSNickeau        return $this->requestedMarkupPath;
31904fd306cSNickeau    }
32004fd306cSNickeau
32104fd306cSNickeau
32204fd306cSNickeau    /**
32304fd306cSNickeau     * The cache stores only public pages.
32404fd306cSNickeau     *
32504fd306cSNickeau     * ie when the user is unknown
32604fd306cSNickeau     * and there is no railbar
32704fd306cSNickeau     * (railbar is dynamically created even for the public
32804fd306cSNickeau     * and the javascript for the menu item expects to run after a window load event)
32904fd306cSNickeau     *
33004fd306cSNickeau     * @return bool
33104fd306cSNickeau     */
33204fd306cSNickeau    private function isPublicStaticPage(): bool
33304fd306cSNickeau    {
33404fd306cSNickeau        $privateRailbar = SiteConfig::getConfValue(FetcherRailBar::CONF_PRIVATE_RAIL_BAR, 0);
33504fd306cSNickeau        $isLoggedIn = Identity::isLoggedIn();
33604fd306cSNickeau        return $privateRailbar === 1 && !$isLoggedIn;
33704fd306cSNickeau    }
33804fd306cSNickeau
33904fd306cSNickeau    private function getRequestedTemplateOrDefault(): string
34004fd306cSNickeau    {
34104fd306cSNickeau        try {
34204fd306cSNickeau            return $this->getRequestedTemplate();
34304fd306cSNickeau        } catch (ExceptionNotFound $e) {
34404fd306cSNickeau            return PageTemplateName::createFromPage($this->getRequestedPage())->getValueOrDefault();
34504fd306cSNickeau        }
34604fd306cSNickeau    }
34704fd306cSNickeau
34804fd306cSNickeau    public function getContentCachePath(): LocalPath
34904fd306cSNickeau    {
35004fd306cSNickeau        $this->buildObjectIfNeeded();
35104fd306cSNickeau        return $this->fetcherCache->getFile();
35204fd306cSNickeau    }
35304fd306cSNickeau
35404fd306cSNickeau    public function getLabel(): string
35504fd306cSNickeau    {
35604fd306cSNickeau        $sourcePath = $this->getSourcePath();
35704fd306cSNickeau        return ResourceName::getFromPath($sourcePath);
35804fd306cSNickeau    }
35904fd306cSNickeau
36004fd306cSNickeau
36104fd306cSNickeau}
362