1<?php
2
3
4// must be run within Dokuwiki
5use ComboStrap\Brand;
6use ComboStrap\BrandButton;
7use ComboStrap\BrandTag;
8use ComboStrap\IconTag;
9use ComboStrap\MarkupCacheDependencies;
10use ComboStrap\CacheManager;
11use ComboStrap\Call;
12use ComboStrap\CallStack;
13use ComboStrap\ColorRgb;
14use ComboStrap\Dimension;
15use ComboStrap\ExceptionCompile;
16use ComboStrap\Icon;
17use ComboStrap\IconDownloader;
18use ComboStrap\LogUtility;
19use ComboStrap\MarkupPath;
20use ComboStrap\MediaMarkup;
21use ComboStrap\PluginUtility;
22use ComboStrap\Site;
23use ComboStrap\TagAttributes;
24use ComboStrap\Template;
25use ComboStrap\XmlTagProcessing;
26
27if (!defined('DOKU_INC')) die();
28
29
30class syntax_plugin_combo_brand extends DokuWiki_Syntax_Plugin
31{
32
33    const TAG = "brand";
34    const CANONICAL = self::TAG;
35
36
37    public static function addOpenLinkTagInCallStack(CallStack $callStack, TagAttributes $tagAttributes)
38    {
39        $linkArrayAttributes = $tagAttributes->toCallStackArray();
40        $linkArrayAttributes[TagAttributes::TYPE_KEY] = $tagAttributes->getLogicalTag();
41        $linkAttributes = TagAttributes::createFromCallStackArray($linkArrayAttributes);
42        syntax_plugin_combo_link::addOpenLinkTagInCallStack($callStack, $linkAttributes);
43    }
44
45
46    /**
47     * Syntax Type.
48     *
49     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
50     * @see DokuWiki_Syntax_Plugin::getType()
51     */
52    function getType(): string
53    {
54        return 'substition';
55    }
56
57    /**
58     * How Dokuwiki will add P element
59     *
60     *  * 'normal' - The plugin can be used inside paragraphs
61     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
62     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
63     *
64     * @see DokuWiki_Syntax_Plugin::getPType()
65     */
66    function getPType(): string
67    {
68        return 'normal';
69    }
70
71    /**
72     * @return array
73     * Allow which kind of plugin inside
74     *
75     * array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
76     *
77     */
78    function getAllowedTypes(): array
79    {
80        return array('baseonly', 'formatting', 'substition', 'protected', 'disabled');
81    }
82
83    function getSort(): int
84    {
85        return 201;
86    }
87
88    public
89    function accepts($mode): bool
90    {
91        return syntax_plugin_combo_preformatted::disablePreformatted($mode);
92    }
93
94
95    /**
96     * Create a pattern that will called this plugin
97     *
98     * @param string $mode
99     * @see Doku_Parser_Mode::connectTo()
100     */
101    function connectTo($mode)
102    {
103
104        $pattern = XmlTagProcessing::getContainerTagPattern(self::getTag());
105        $this->Lexer->addEntryPattern($pattern, $mode, 'plugin_' . PluginUtility::PLUGIN_BASE_NAME . '_' . $this->getPluginComponent());
106
107        /**
108         * The empty tag pattern should be after the container pattern
109         */
110        $this->Lexer->addSpecialPattern(PluginUtility::getEmptyTagPattern(self::TAG), $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
111
112    }
113
114    function postConnect()
115    {
116
117        $this->Lexer->addExitPattern('</' . self::getTag() . '>', 'plugin_' . PluginUtility::PLUGIN_BASE_NAME . '_' . $this->getPluginComponent());
118
119    }
120
121    function handle($match, $state, $pos, Doku_Handler $handler)
122    {
123
124
125        switch ($state) {
126
127            case DOKU_LEXER_SPECIAL :
128            case DOKU_LEXER_ENTER :
129
130                /**
131                 * Tag building
132                 */
133                $defaultAttributes = [TagAttributes::TYPE_KEY => Brand::CURRENT_BRAND];
134                $tagAttributes = TagAttributes::createFromTagMatch($match, $defaultAttributes, [], true)
135                    ->setLogicalTag(BrandTag::MARKUP);
136
137                /**
138                 * Extra properties
139                 */
140                $returnedArray = BrandTag::handleSpecialEnter($tagAttributes, $handler);
141
142                /**
143                 * Common properties
144                 */
145                $returnedArray[PluginUtility::STATE] = $state;
146                $returnedArray[PluginUtility::ATTRIBUTES] = $tagAttributes->toCallStackArray();
147                return $returnedArray;
148
149            case DOKU_LEXER_UNMATCHED :
150                return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler);
151
152            case DOKU_LEXER_EXIT :
153
154                $callStack = CallStack::createFromHandler($handler);
155                $openTag = $callStack->moveToPreviousCorrespondingOpeningCall();
156                $openTagAttributes = TagAttributes::createFromCallStackArray($openTag->getAttributes());
157                $openTagContext = $openTag->getContext();
158                /**
159                 * Old syntax
160                 * An icon/image could be already inside
161                 * We go from end to start to
162                 * see if there is also a text, if this is the case,
163                 * there is a class added on the media
164                 */
165                $markupIconImageFound = false;
166                $textFound = false;
167                $callStack->moveToEnd();
168                while ($actualCall = $callStack->previous()) {
169                    $tagName = $actualCall->getTagName();
170                    if (in_array($tagName, [IconTag::TAG, syntax_plugin_combo_media::TAG])) {
171
172
173                        if ($textFound && $openTagContext === syntax_plugin_combo_menubar::TAG) {
174                            // if text and icon
175                            // We add it here because, if they are present, we don't add them later
176                            // for all on raster image
177                            $actualCall->addClassName(BrandTag::BOOTSTRAP_NAV_BAR_IMAGE_AND_TEXT_CLASS);
178                        }
179
180                        // is it a added call / no content
181                        // or is it an icon from the markup
182                        if ($actualCall->getCapturedContent() === null) {
183
184                            // It's an added call
185                            // No user icon, image can be found anymore
186                            // exiting
187                            break;
188                        }
189
190                        $primary = $openTagAttributes->getValue(ColorRgb::PRIMARY_VALUE);
191                        if ($primary !== null && $tagName === IconTag::TAG) {
192                            try {
193                                $brandButton = BrandTag::createButtonFromAttributes($openTagAttributes);
194                                $actualCall->addAttribute(ColorRgb::COLOR, $brandButton->getTextColor());
195                            } catch (ExceptionCompile $e) {
196                                LogUtility::msg("Error while trying to set the icon color on exit. Error: {$e->getMessage()}");
197                            }
198                        }
199
200
201                        // no linking inside a brand
202                        $actualCall->addAttribute(MediaMarkup::LINKING_KEY, MediaMarkup::LINKING_NOLINK_VALUE);
203                        $markupIconImageFound = true;
204
205                    }
206                    if ($actualCall->getState() === DOKU_LEXER_UNMATCHED) {
207                        $textFound = true;
208                    }
209                }
210                $openTag->setPluginData(BrandTag::BRAND_IMAGE_FOUND_INDICATOR, $markupIconImageFound);
211                $openTag->setPluginData(BrandTag::BRAND_TEXT_FOUND_INDICATOR, $textFound);
212
213                return array(
214                    PluginUtility::STATE => $state
215                );
216
217
218        }
219        return array();
220
221    }
222
223    /**
224     * Render the output
225     * @param string $format
226     * @param Doku_Renderer $renderer
227     * @param array $data - what the function handle() return
228     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
229     * @see DokuWiki_Syntax_Plugin::render()
230     *
231     *
232     */
233    function render($format, Doku_Renderer $renderer, $data): bool
234    {
235
236        if ($format === "xhtml") {
237            $state = $data[PluginUtility::STATE];
238            switch ($state) {
239                case DOKU_LEXER_SPECIAL:
240                case DOKU_LEXER_ENTER:
241                    $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]);
242                    $renderer->doc .= BrandTag::render($tagAttributes, $state, $data);;
243                    break;
244                case DOKU_LEXER_UNMATCHED:
245                    $renderer->doc .= PluginUtility::renderUnmatched($data);
246                    break;
247                case DOKU_LEXER_EXIT:
248                    $renderer->doc .= "</a>";
249                    break;
250
251            }
252            return true;
253        }
254
255        // unsupported $mode
256        return false;
257    }
258
259    public
260    static function getTag(): string
261    {
262        return self::TAG;
263    }
264
265    /**
266     *
267     * @throws ExceptionCompile
268     */
269    public
270    static function addIconInCallStack(CallStack $callStack, BrandButton $brandButton)
271    {
272
273        if (!$brandButton->hasIcon()) {
274            return;
275        }
276        $iconAttributes = $brandButton->getIconAttributes();
277
278        $callStack->appendCallAtTheEnd(
279            Call::createComboCall(
280                IconTag::TAG,
281                DOKU_LEXER_SPECIAL,
282                $iconAttributes
283            ));
284    }
285
286}
287
288