1<?php
2
3
4use ComboStrap\ExceptionNotFound;
5use ComboStrap\ExecutionContext;
6use ComboStrap\FragmentTag;
7use ComboStrap\MarkupCacheDependencies;
8use ComboStrap\CacheManager;
9use ComboStrap\CallStack;
10use ComboStrap\Canonical;
11use ComboStrap\ExceptionCompile;
12use ComboStrap\LogUtility;
13use ComboStrap\MarkupPath;
14use ComboStrap\CreationDate;
15use ComboStrap\PagePath;
16use ComboStrap\PagePublicationDate;
17use ComboStrap\PluginUtility;
18use ComboStrap\MarkupRenderUtility;
19use ComboStrap\ResourceName;
20use ComboStrap\TagAttributes;
21use ComboStrap\XmlTagProcessing;
22
23
24require_once(__DIR__ . "/../ComboStrap/PluginUtility.php");
25
26/**
27 *
28 * Fragment
29 *
30 * A fragment is a part of a markup file
31 * that captures the instructions
32 * and then render them with {@link \ComboStrap\FetcherMarkup}
33 * with different {@link \ComboStrap\FetcherMarkupBuilder::setContextData() context data}
34 * for each page.
35 *
36 * It should be used inside an iterator.
37 *
38 *
39 *
40 */
41class syntax_plugin_combo_fragment extends DokuWiki_Syntax_Plugin
42{
43
44
45    /**
46     * Syntax Type.
47     *
48     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
49     * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types
50     * @see DokuWiki_Syntax_Plugin::getType()
51     */
52    function getType(): string
53    {
54        return 'formatting';
55    }
56
57    /**
58     * How Dokuwiki will add P element
59     *
60     * * 'normal' - Inline
61     *  * 'block' - Block (p are not created inside)
62     *  * 'stack' - Block (p can be created inside)
63     *
64     * @see DokuWiki_Syntax_Plugin::getPType()
65     * @see https://www.dokuwiki.org/devel:syntax_plugins#ptype
66     */
67    function getPType(): string
68    {
69        /**
70         * No P please
71         */
72        return 'normal';
73    }
74
75    /**
76     * @return array
77     * Allow which kind of plugin inside
78     *
79     * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
80     * because we manage self the content and we call self the parser
81     *
82     * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php
83     */
84    function getAllowedTypes(): array
85    {
86
87        return array('baseonly', 'container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs');
88    }
89
90    function getSort(): int
91    {
92        return 201;
93    }
94
95    public function accepts($mode)
96    {
97        return syntax_plugin_combo_preformatted::disablePreformatted($mode);
98    }
99
100
101    function connectTo($mode)
102    {
103
104        foreach (FragmentTag::TAGS as $tag) {
105            $pattern = XmlTagProcessing::getContainerTagPattern($tag);
106            $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
107        }
108
109
110    }
111
112
113    public function postConnect()
114    {
115        foreach (FragmentTag::TAGS as $tag) {
116            $this->Lexer->addExitPattern('</' . $tag . '>', PluginUtility::getModeFromTag($this->getPluginComponent()));
117        }
118
119
120    }
121
122
123    /**
124     *
125     * The handle function goal is to parse the matched syntax through the pattern function
126     * and to return the result for use in the renderer
127     * This result is always cached until the page is modified.
128     * @param string $match
129     * @param int $state
130     * @param int $pos - byte position in the original source file
131     * @param Doku_Handler $handler
132     * @return array
133     * @throws Exception
134     * @see DokuWiki_Syntax_Plugin::handle()
135     *
136     */
137    function handle($match, $state, $pos, Doku_Handler $handler): array
138    {
139
140        switch ($state) {
141
142            case DOKU_LEXER_ENTER :
143
144                if (substr($match, 1, strlen(FragmentTag::TEMPLATE_TAG)) === FragmentTag::TEMPLATE_TAG) {
145                    LogUtility::warning("The template component has been deprecated and replaced by the fragment component. Why ? Because a whole page is now a template. ", syntax_plugin_combo_iterator::CANONICAL);
146                }
147                $tagAttributes = TagAttributes::createFromTagMatch($match);
148                return array(
149                    PluginUtility::STATE => $state,
150                    PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray()
151                );
152
153            case DOKU_LEXER_UNMATCHED :
154
155                // We should not ever come here but a user does not not known that
156                return PluginUtility::handleAndReturnUnmatchedData(FragmentTag::FRAGMENT_TAG, $match, $handler);
157
158
159            case DOKU_LEXER_EXIT :
160
161                /**
162                 * Gather template stack
163                 */
164                $callStack = CallStack::createFromHandler($handler);
165                $templateEnterCall = $callStack->moveToPreviousCorrespondingOpeningCall();
166                $templateStack = [];
167                while ($actualCall = $callStack->next()) {
168                    $templateStack[] = $actualCall->toCallArray();
169                }
170                $callStack->deleteAllCallsAfter($templateEnterCall);
171
172                /**
173                 * Cache dependent on the requested page
174                 */
175                try {
176                    ExecutionContext::getActualOrCreateFromEnv()
177                        ->getExecutingMarkupHandler()
178                        ->getOutputCacheDependencies()
179                        ->addDependency(MarkupCacheDependencies::REQUESTED_PAGE_DEPENDENCY);
180                } catch (ExceptionNotFound $e) {
181                    // not a fetcher markup run
182                }
183
184
185                return array(
186                    PluginUtility::STATE => $state,
187                    FragmentTag::CALLSTACK => $templateStack
188                );
189
190
191        }
192        return array();
193
194    }
195
196    /**
197     * Render the output
198     * @param string $format
199     * @param Doku_Renderer $renderer
200     * @param array $data - what the function handle() return'ed
201     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
202     * @throws ExceptionNotFound
203     * @see DokuWiki_Syntax_Plugin::render()
204     *
205     *
206     */
207    function render($format, Doku_Renderer $renderer, $data): bool
208    {
209
210        if ($format === "xhtml") {
211            $state = $data[PluginUtility::STATE];
212            switch ($state) {
213                case DOKU_LEXER_UNMATCHED:
214                    $renderer->doc .= PluginUtility::renderUnmatched($data);
215                    return true;
216                case DOKU_LEXER_EXIT:
217                    $templateStack = $data[FragmentTag::CALLSTACK];
218                    if ($templateStack === null) {
219                        $renderer->doc .= LogUtility::wrapInRedForHtml("Template instructions should not be null");
220                        return false;
221                    }
222                    $page = MarkupPath::createFromRequestedPage();
223                    $metadata = $page->getMetadataForRendering();
224                    try {
225                        $renderer->doc .= MarkupRenderUtility::renderInstructionsToXhtml($templateStack, $metadata);
226                    } catch (ExceptionCompile $e) {
227                        $renderer->doc .= LogUtility::wrapInRedForHtml("Error while rendering the instruction. Error: {$e->getMessage()}");
228                    }
229                    LogUtility::warning("There is no need anymore to use a template to render variable", FragmentTag::CANONICAL);
230                    return true;
231            }
232        }
233        return false;
234
235    }
236
237
238}
239
240