xref: /template/strap/ComboStrap/FetcherRailBar.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace ComboStrap;
4*04fd306cSNickeau
5*04fd306cSNickeauuse ComboStrap\TagAttribute\StyleAttribute;
6*04fd306cSNickeauuse dokuwiki\Menu\PageMenu;
7*04fd306cSNickeauuse dokuwiki\Menu\SiteMenu;
8*04fd306cSNickeauuse dokuwiki\Menu\UserMenu;
9*04fd306cSNickeau
10*04fd306cSNickeau/**
11*04fd306cSNickeau * A fetcher for a menu rail bar
12*04fd306cSNickeau * https://material.io/components/navigation-rail
13*04fd306cSNickeau *
14*04fd306cSNickeau *
15*04fd306cSNickeau * Note: this class is a fetcher but it does not still work to call it via a javascript function included in the page.
16*04fd306cSNickeau * Why ? The problem is that plugins that add a item would expect
17*04fd306cSNickeau * to be loaded with the page and the related javascript is generally wrapped in a listener waiting for the page load event.
18*04fd306cSNickeau * It means that it would never be triggered.
19*04fd306cSNickeau *
20*04fd306cSNickeau */
21*04fd306cSNickeauclass FetcherRailBar extends IFetcherAbs implements IFetcherString
22*04fd306cSNickeau{
23*04fd306cSNickeau
24*04fd306cSNickeau    use FetcherTraitWikiPath;
25*04fd306cSNickeau
26*04fd306cSNickeau    const CANONICAL = self::NAME;
27*04fd306cSNickeau    const NAME = "railbar";
28*04fd306cSNickeau    const FIXED_LAYOUT = "fixed";
29*04fd306cSNickeau    const OFFCANVAS_LAYOUT = "off-canvas";
30*04fd306cSNickeau    const VIEWPORT_WIDTH = "viewport";
31*04fd306cSNickeau    const LAYOUT_ATTRIBUTE = "layout";
32*04fd306cSNickeau    /**
33*04fd306cSNickeau     * Do we show the rail bar for anonymous user
34*04fd306cSNickeau     */
35*04fd306cSNickeau    public const CONF_PRIVATE_RAIL_BAR = "privateRailbar";
36*04fd306cSNickeau    /**
37*04fd306cSNickeau     * When do we toggle from offcanvas to fixed railbar
38*04fd306cSNickeau     */
39*04fd306cSNickeau    public const CONF_BREAKPOINT_RAIL_BAR = "breakpointRailbar";
40*04fd306cSNickeau    const BOTH_LAYOUT = "all";
41*04fd306cSNickeau    const KNOWN_LAYOUT = [self::FIXED_LAYOUT, self::OFFCANVAS_LAYOUT, self::BOTH_LAYOUT];
42*04fd306cSNickeau
43*04fd306cSNickeau
44*04fd306cSNickeau    private int $requestedViewPort;
45*04fd306cSNickeau    private string $requestedLayout;
46*04fd306cSNickeau
47*04fd306cSNickeau
48*04fd306cSNickeau    public static function createRailBar(): FetcherRailBar
49*04fd306cSNickeau    {
50*04fd306cSNickeau        return new FetcherRailBar();
51*04fd306cSNickeau    }
52*04fd306cSNickeau
53*04fd306cSNickeau    private static function getComponentClass(): string
54*04fd306cSNickeau    {
55*04fd306cSNickeau        return StyleAttribute::addComboStrapSuffix(self::CANONICAL);
56*04fd306cSNickeau    }
57*04fd306cSNickeau
58*04fd306cSNickeau    /**
59*04fd306cSNickeau     * @throws ExceptionBadArgument
60*04fd306cSNickeau     * @throws ExceptionBadSyntax
61*04fd306cSNickeau     * @throws ExceptionNotExists
62*04fd306cSNickeau     * @throws ExceptionNotFound
63*04fd306cSNickeau     */
64*04fd306cSNickeau    public function buildFromTagAttributes(TagAttributes $tagAttributes): IFetcher
65*04fd306cSNickeau    {
66*04fd306cSNickeau        /**
67*04fd306cSNickeau         * Capture the id
68*04fd306cSNickeau         */
69*04fd306cSNickeau        $this->buildOriginalPathFromTagAttributes($tagAttributes);
70*04fd306cSNickeau        /**
71*04fd306cSNickeau         * Capture the view port
72*04fd306cSNickeau         */
73*04fd306cSNickeau        $viewPortWidth = $tagAttributes->getValueAndRemoveIfPresent(self::VIEWPORT_WIDTH);
74*04fd306cSNickeau        if ($viewPortWidth !== null) {
75*04fd306cSNickeau            try {
76*04fd306cSNickeau                $this->setRequestedViewPort(DataType::toInteger($viewPortWidth));
77*04fd306cSNickeau            } catch (ExceptionBadArgument $e) {
78*04fd306cSNickeau                throw new ExceptionBadArgument("The viewport width is not a valid integer. Error:{$e->getMessage()}", self::CANONICAL);
79*04fd306cSNickeau            }
80*04fd306cSNickeau        }
81*04fd306cSNickeau        /**
82*04fd306cSNickeau         * Capture the layout
83*04fd306cSNickeau         */
84*04fd306cSNickeau        $layout = $tagAttributes->getValueAndRemoveIfPresent(self::LAYOUT_ATTRIBUTE);
85*04fd306cSNickeau        if ($layout !== null) {
86*04fd306cSNickeau            try {
87*04fd306cSNickeau                $this->setRequestedLayout($layout);
88*04fd306cSNickeau            } catch (ExceptionBadArgument $e) {
89*04fd306cSNickeau                throw new ExceptionBadArgument("The layout is not a valid. Error:{$e->getMessage()}", self::CANONICAL);
90*04fd306cSNickeau            }
91*04fd306cSNickeau        }
92*04fd306cSNickeau        return parent::buildFromTagAttributes($tagAttributes);
93*04fd306cSNickeau    }
94*04fd306cSNickeau
95*04fd306cSNickeau
96*04fd306cSNickeau    function getFetchPath(): Path
97*04fd306cSNickeau    {
98*04fd306cSNickeau        throw new ExceptionRuntimeInternal("No fetch path: Railbar is not a file but a dynamic HTML document");
99*04fd306cSNickeau    }
100*04fd306cSNickeau
101*04fd306cSNickeau    function getFetchString(): string
102*04fd306cSNickeau    {
103*04fd306cSNickeau
104*04fd306cSNickeau        if (!$this->shouldBePrinted()) {
105*04fd306cSNickeau            return "";
106*04fd306cSNickeau        }
107*04fd306cSNickeau
108*04fd306cSNickeau        $localWikiRequest = null;
109*04fd306cSNickeau        $localWikiId = null;
110*04fd306cSNickeau        try {
111*04fd306cSNickeau            ExecutionContext::getExecutionContext();
112*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
113*04fd306cSNickeau
114*04fd306cSNickeau            /**
115*04fd306cSNickeau             * No actual request (called via ajax)
116*04fd306cSNickeau             */
117*04fd306cSNickeau            $localWikiId = $this->getSourcePath()->getWikiId();
118*04fd306cSNickeau            $localWikiRequest = ExecutionContext::getOrCreateFromRequestedWikiId($localWikiId);
119*04fd306cSNickeau
120*04fd306cSNickeau            /**
121*04fd306cSNickeau             * page info is needed and used by all other plugins
122*04fd306cSNickeau             * in all hooks (should be first)
123*04fd306cSNickeau             */
124*04fd306cSNickeau            global $INFO;
125*04fd306cSNickeau            $INFO = pageinfo();
126*04fd306cSNickeau
127*04fd306cSNickeau            /**
128*04fd306cSNickeau             * Uses by {@link action_plugin_move_rename} to set
129*04fd306cSNickeau             * if it will add the button
130*04fd306cSNickeau             */
131*04fd306cSNickeau            $tmp = array();
132*04fd306cSNickeau            \dokuwiki\Extension\Event::createAndTrigger('DOKUWIKI_STARTED', $tmp);
133*04fd306cSNickeau
134*04fd306cSNickeau        }
135*04fd306cSNickeau
136*04fd306cSNickeau
137*04fd306cSNickeau        try {
138*04fd306cSNickeau
139*04fd306cSNickeau            $snippetManager = SnippetSystem::getFromContext();
140*04fd306cSNickeau            $railBarHtmlListItems = $this->getRailBarHtmlListItems();
141*04fd306cSNickeau            $railBarLayout = $this->getLayoutTypeToApply();
142*04fd306cSNickeau            switch ($railBarLayout) {
143*04fd306cSNickeau                case self::FIXED_LAYOUT:
144*04fd306cSNickeau                    $railBar = $this->toFixedLayout($railBarHtmlListItems);
145*04fd306cSNickeau                    $snippetManager->attachCssInternalStylesheet("railbar-$railBarLayout");
146*04fd306cSNickeau                    break;
147*04fd306cSNickeau                case self::OFFCANVAS_LAYOUT:
148*04fd306cSNickeau                    $railBar = $this->toOffCanvasLayout($railBarHtmlListItems);
149*04fd306cSNickeau                    $snippetManager->attachCssInternalStylesheet("railbar-$railBarLayout");
150*04fd306cSNickeau                    break;
151*04fd306cSNickeau                case self::BOTH_LAYOUT:
152*04fd306cSNickeau                default:
153*04fd306cSNickeau                    $snippetManager->attachCssInternalStylesheet("railbar-" . self::FIXED_LAYOUT);
154*04fd306cSNickeau                    $snippetManager->attachCssInternalStylesheet("railbar-" . self::OFFCANVAS_LAYOUT);
155*04fd306cSNickeau                    $breakpoint = $this->getBreakPointConfiguration();
156*04fd306cSNickeau                    $railBar = $this->toFixedLayout($railBarHtmlListItems, $breakpoint)
157*04fd306cSNickeau                        . $this->toOffCanvasLayout($railBarHtmlListItems, $breakpoint);
158*04fd306cSNickeau                    break;
159*04fd306cSNickeau            }
160*04fd306cSNickeau
161*04fd306cSNickeau
162*04fd306cSNickeau            $snippetManager->attachCssInternalStylesheet("railbar");
163*04fd306cSNickeau
164*04fd306cSNickeau            if ($localWikiRequest !== null) {
165*04fd306cSNickeau                $snippets = $snippetManager->toHtmlForAllSnippets();
166*04fd306cSNickeau                $snippetClass = self::getSnippetClass();
167*04fd306cSNickeau                /**
168*04fd306cSNickeau                 * Snippets should be after the html because they works
169*04fd306cSNickeau                 * on the added HTML
170*04fd306cSNickeau                 */
171*04fd306cSNickeau                $railBar = <<<EOF
172*04fd306cSNickeau$railBar
173*04fd306cSNickeau<div id="$snippetClass" class="$snippetClass">
174*04fd306cSNickeau$snippets
175*04fd306cSNickeau</div>
176*04fd306cSNickeauEOF;
177*04fd306cSNickeau            }
178*04fd306cSNickeau
179*04fd306cSNickeau            return $railBar;
180*04fd306cSNickeau
181*04fd306cSNickeau
182*04fd306cSNickeau        } finally {
183*04fd306cSNickeau            if ($localWikiRequest !== null) {
184*04fd306cSNickeau                $localWikiRequest->close($localWikiId);
185*04fd306cSNickeau            }
186*04fd306cSNickeau        }
187*04fd306cSNickeau
188*04fd306cSNickeau    }
189*04fd306cSNickeau
190*04fd306cSNickeau    function getBuster(): string
191*04fd306cSNickeau    {
192*04fd306cSNickeau        return "";
193*04fd306cSNickeau    }
194*04fd306cSNickeau
195*04fd306cSNickeau    public function getMime(): Mime
196*04fd306cSNickeau    {
197*04fd306cSNickeau        return Mime::getHtml();
198*04fd306cSNickeau    }
199*04fd306cSNickeau
200*04fd306cSNickeau    public function getFetcherName(): string
201*04fd306cSNickeau    {
202*04fd306cSNickeau        return self::NAME;
203*04fd306cSNickeau    }
204*04fd306cSNickeau
205*04fd306cSNickeau    public function setRequestedPageWikiId(string $wikiId): FetcherRailBar
206*04fd306cSNickeau    {
207*04fd306cSNickeau        $path = WikiPath::createMarkupPathFromId($wikiId);
208*04fd306cSNickeau        return $this->setRequestedPath($path);
209*04fd306cSNickeau    }
210*04fd306cSNickeau
211*04fd306cSNickeau    public static function getSnippetClass(): string
212*04fd306cSNickeau    {
213*04fd306cSNickeau        return Snippet::getClassFromComponentId(self::CANONICAL);
214*04fd306cSNickeau    }
215*04fd306cSNickeau
216*04fd306cSNickeau    private function getRailBarHtmlListItems(): string
217*04fd306cSNickeau    {
218*04fd306cSNickeau        $liUserTools = (new UserMenu())->getListItems('action');
219*04fd306cSNickeau        $pageMenu = new PageMenu();
220*04fd306cSNickeau        $liPageTools = $pageMenu->getListItems();
221*04fd306cSNickeau        $liSiteTools = (new SiteMenu())->getListItems('action');
222*04fd306cSNickeau        // FYI: The below code outputs all menu in mobile (in another HTML layout)
223*04fd306cSNickeau        // echo (new \dokuwiki\Menu\MobileMenu())->getDropdown($lang['tools']);
224*04fd306cSNickeau        $componentClass = self::getComponentClass();
225*04fd306cSNickeau        return <<<EOF
226*04fd306cSNickeau<ul class="$componentClass">
227*04fd306cSNickeau    <li><a href="#" style="height: 19px;line-height: 17px;text-align: left;font-weight:bold"><span>User</span><svg style="height:19px"></svg></a></li>
228*04fd306cSNickeau    $liUserTools
229*04fd306cSNickeau    <li><a href="#" style="height: 19px;line-height: 17px;text-align: left;font-weight:bold"><span>Page</span><svg style="height:19px"></svg></a></li>
230*04fd306cSNickeau    $liPageTools
231*04fd306cSNickeau    <li><a href="#" style="height: 19px;line-height: 17px;text-align: left;font-weight:bold"><span>Website</span><svg style="height:19px"></svg></a></li>
232*04fd306cSNickeau    $liSiteTools
233*04fd306cSNickeau</ul>
234*04fd306cSNickeauEOF;
235*04fd306cSNickeau
236*04fd306cSNickeau    }
237*04fd306cSNickeau
238*04fd306cSNickeau    private function toOffCanvasLayout(string $railBarHtmlListItems, Breakpoint $hideFromBreakpoint = null): string
239*04fd306cSNickeau    {
240*04fd306cSNickeau        $breakpointHiding = "";
241*04fd306cSNickeau        if ($hideFromBreakpoint !== null) {
242*04fd306cSNickeau            $breakpointHiding = "d-{$hideFromBreakpoint->getShortName()}-none";
243*04fd306cSNickeau        }
244*04fd306cSNickeau        $railBarOffCanvasPrefix = "railbar-offcanvas";
245*04fd306cSNickeau        $railBarClass = StyleAttribute::addComboStrapSuffix(self::NAME);
246*04fd306cSNickeau        $railBarOffCanvasClassAndId = StyleAttribute::addComboStrapSuffix($railBarOffCanvasPrefix);
247*04fd306cSNickeau        $railBarOffCanvasWrapperId = StyleAttribute::addComboStrapSuffix("{$railBarOffCanvasPrefix}-wrapper");
248*04fd306cSNickeau        $railBarOffCanvasLabelId = StyleAttribute::addComboStrapSuffix("{$railBarOffCanvasPrefix}-label");
249*04fd306cSNickeau        $railBarOffcanvasBodyId = StyleAttribute::addComboStrapSuffix("{$railBarOffCanvasPrefix}-body");
250*04fd306cSNickeau        $railBarOffCanvasCloseId = StyleAttribute::addComboStrapSuffix("{$railBarOffCanvasPrefix}-close");
251*04fd306cSNickeau        $railBarOffCanvasOpenId = StyleAttribute::addComboStrapSuffix("{$railBarOffCanvasPrefix}-open");
252*04fd306cSNickeau        return <<<EOF
253*04fd306cSNickeau<div id="$railBarOffCanvasWrapperId" class="$railBarClass $railBarOffCanvasClassAndId $breakpointHiding">
254*04fd306cSNickeau    <button id="$railBarOffCanvasOpenId" class="btn" type="button" data-bs-toggle="offcanvas"
255*04fd306cSNickeau            data-bs-target="#$railBarOffCanvasClassAndId" aria-controls="railbar-offcanvas">
256*04fd306cSNickeau    </button>
257*04fd306cSNickeau
258*04fd306cSNickeau    <div id="$railBarOffCanvasClassAndId" class="offcanvas offcanvas-end" aria-labelledby="$railBarOffCanvasLabelId"
259*04fd306cSNickeau         style="visibility: hidden;" aria-hidden="true">
260*04fd306cSNickeau         <h5 class="d-none" id="$railBarOffCanvasLabelId">Railbar</h5>
261*04fd306cSNickeau        <!-- Pseudo relative element  https://stackoverflow.com/questions/6040005/relatively-position-an-element-without-it-taking-up-space-in-document-flow -->
262*04fd306cSNickeau        <div style="position: relative; width: 0; height: 0">
263*04fd306cSNickeau            <button id="$railBarOffCanvasCloseId" class="btn" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button>
264*04fd306cSNickeau        </div>
265*04fd306cSNickeau        <div id="$railBarOffcanvasBodyId" class="offcanvas-body" style="align-items: center;display: flex;">
266*04fd306cSNickeau            $railBarHtmlListItems
267*04fd306cSNickeau        </div>
268*04fd306cSNickeau    </div>
269*04fd306cSNickeau</div>
270*04fd306cSNickeauEOF;
271*04fd306cSNickeau
272*04fd306cSNickeau    }
273*04fd306cSNickeau
274*04fd306cSNickeau    public function getLayoutTypeToApply(): string
275*04fd306cSNickeau    {
276*04fd306cSNickeau
277*04fd306cSNickeau        if (isset($this->requestedLayout)) {
278*04fd306cSNickeau            return $this->requestedLayout;
279*04fd306cSNickeau        }
280*04fd306cSNickeau        $bootstrapVersion = Bootstrap::getBootStrapMajorVersion();
281*04fd306cSNickeau        if ($bootstrapVersion === Bootstrap::BootStrapFourMajorVersion) {
282*04fd306cSNickeau            return self::FIXED_LAYOUT;
283*04fd306cSNickeau        }
284*04fd306cSNickeau        try {
285*04fd306cSNickeau            $breakPointConfigurationInPixel = $this->getBreakPointConfiguration()->getWidth();
286*04fd306cSNickeau        } catch (ExceptionInfinite $e) {
287*04fd306cSNickeau            // no breakpoint
288*04fd306cSNickeau            return self::OFFCANVAS_LAYOUT;
289*04fd306cSNickeau        }
290*04fd306cSNickeau
291*04fd306cSNickeau        try {
292*04fd306cSNickeau            if ($this->getRequestedViewPort() > $breakPointConfigurationInPixel) {
293*04fd306cSNickeau                return self::FIXED_LAYOUT;
294*04fd306cSNickeau            } else {
295*04fd306cSNickeau                return self::OFFCANVAS_LAYOUT;
296*04fd306cSNickeau            }
297*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
298*04fd306cSNickeau            // no known target view port
299*04fd306cSNickeau            // we send them both then
300*04fd306cSNickeau            return self::BOTH_LAYOUT;
301*04fd306cSNickeau        }
302*04fd306cSNickeau
303*04fd306cSNickeau    }
304*04fd306cSNickeau
305*04fd306cSNickeau    public function setRequestedViewPort(int $viewPort): FetcherRailBar
306*04fd306cSNickeau    {
307*04fd306cSNickeau        $this->requestedViewPort = $viewPort;
308*04fd306cSNickeau        return $this;
309*04fd306cSNickeau    }
310*04fd306cSNickeau
311*04fd306cSNickeau    /**
312*04fd306cSNickeau     * The call may indicate the view port that the railbar will be used for
313*04fd306cSNickeau     * (ie breakpoint)
314*04fd306cSNickeau     * @return int
315*04fd306cSNickeau     * @throws ExceptionNotFound
316*04fd306cSNickeau     */
317*04fd306cSNickeau    public function getRequestedViewPort(): int
318*04fd306cSNickeau    {
319*04fd306cSNickeau        if (!isset($this->requestedViewPort)) {
320*04fd306cSNickeau            throw new ExceptionNotFound("No requested view port");
321*04fd306cSNickeau        }
322*04fd306cSNickeau        return $this->requestedViewPort;
323*04fd306cSNickeau    }
324*04fd306cSNickeau
325*04fd306cSNickeau    private function shouldBePrinted(): bool
326*04fd306cSNickeau    {
327*04fd306cSNickeau
328*04fd306cSNickeau        if (
329*04fd306cSNickeau            SiteConfig::getConfValue(self::CONF_PRIVATE_RAIL_BAR, 0) === 1
330*04fd306cSNickeau            && !Identity::isLoggedIn()
331*04fd306cSNickeau        ) {
332*04fd306cSNickeau            return false;
333*04fd306cSNickeau        }
334*04fd306cSNickeau        return true;
335*04fd306cSNickeau
336*04fd306cSNickeau    }
337*04fd306cSNickeau
338*04fd306cSNickeau    private function getBreakPointConfiguration(): Breakpoint
339*04fd306cSNickeau    {
340*04fd306cSNickeau        $name = SiteConfig::getConfValue(self::CONF_BREAKPOINT_RAIL_BAR, Breakpoint::BREAKPOINT_LARGE_NAME);
341*04fd306cSNickeau        return Breakpoint::createFromLongName($name);
342*04fd306cSNickeau    }
343*04fd306cSNickeau
344*04fd306cSNickeau
345*04fd306cSNickeau    /**
346*04fd306cSNickeau     * @param string $railBarHtmlListItems
347*04fd306cSNickeau     * @param Breakpoint|null $showFromBreakpoint
348*04fd306cSNickeau     * @return string
349*04fd306cSNickeau     */
350*04fd306cSNickeau    private function toFixedLayout(string $railBarHtmlListItems, Breakpoint $showFromBreakpoint = null): string
351*04fd306cSNickeau    {
352*04fd306cSNickeau        $showFromBreakpointClasses = "";
353*04fd306cSNickeau        if ($showFromBreakpoint !== null) {
354*04fd306cSNickeau            $showFromBreakpointClasses = "d-none d-{$showFromBreakpoint->getShortName()}-flex";
355*04fd306cSNickeau        }
356*04fd306cSNickeau        $railBarClass = StyleAttribute::addComboStrapSuffix(self::NAME);
357*04fd306cSNickeau        $railBarFixedClassOrId = StyleAttribute::addComboStrapSuffix(self::NAME . "-fixed");
358*04fd306cSNickeau        $zIndexRailbar = 1000; // A navigation bar (below the drop down because we use it in the search box for auto-completion)
359*04fd306cSNickeau        return <<<EOF
360*04fd306cSNickeau<div id="$railBarFixedClassOrId" class="$railBarClass $railBarFixedClassOrId d-flex $showFromBreakpointClasses" style="z-index: $zIndexRailbar;">
361*04fd306cSNickeau    <div>
362*04fd306cSNickeau        $railBarHtmlListItems
363*04fd306cSNickeau    </div>
364*04fd306cSNickeau</div>
365*04fd306cSNickeauEOF;
366*04fd306cSNickeau
367*04fd306cSNickeau    }
368*04fd306cSNickeau
369*04fd306cSNickeau    /**
370*04fd306cSNickeau     * The layout may be requested (example in a landing page where you don't want to see it)
371*04fd306cSNickeau     * @param string $layout
372*04fd306cSNickeau     * @return FetcherRailBar
373*04fd306cSNickeau     * @throws ExceptionBadArgument
374*04fd306cSNickeau     */
375*04fd306cSNickeau    public function setRequestedLayout(string $layout): FetcherRailBar
376*04fd306cSNickeau    {
377*04fd306cSNickeau        if (!in_array($layout, self::KNOWN_LAYOUT)) {
378*04fd306cSNickeau            throw new ExceptionBadArgument("The layout ($layout) is not valid. The known-layout are : ".ArrayUtility::formatAsString(self::KNOWN_LAYOUT));
379*04fd306cSNickeau        }
380*04fd306cSNickeau        $this->requestedLayout = $layout;
381*04fd306cSNickeau        return $this;
382*04fd306cSNickeau    }
383*04fd306cSNickeau
384*04fd306cSNickeau    public function setRequestedPath(WikiPath $requestedPath): FetcherRailBar
385*04fd306cSNickeau    {
386*04fd306cSNickeau        $this->setSourcePath($requestedPath);
387*04fd306cSNickeau        return $this;
388*04fd306cSNickeau    }
389*04fd306cSNickeau
390*04fd306cSNickeau
391*04fd306cSNickeau    public function getLabel(): string
392*04fd306cSNickeau    {
393*04fd306cSNickeau        return self::NAME;
394*04fd306cSNickeau    }
395*04fd306cSNickeau
396*04fd306cSNickeau}
397