xref: /plugin/catmenu/action/prosemirror.php (revision aa591c9040aa9d58df44eaf65df693766613dc9f)
16983cdfdSLORTET<?php
26983cdfdSLORTET
36983cdfdSLORTETuse dokuwiki\plugin\catmenu\parser\CatmenuNode;
46983cdfdSLORTETuse dokuwiki\plugin\prosemirror\schema\Node;
56983cdfdSLORTET
66983cdfdSLORTETclass action_plugin_catmenu_prosemirror extends \dokuwiki\Extension\ActionPlugin
76983cdfdSLORTET{
8*aa591c90SLORTET    /** @var helper_plugin_catmenu_namespace|null */
9*aa591c90SLORTET    private $nsHelper = null;
10*aa591c90SLORTET
11*aa591c90SLORTET    /**
12*aa591c90SLORTET     * Retourne le helper namespace (chargé en lazy).
13*aa591c90SLORTET     */
14*aa591c90SLORTET    private function getNsHelper(): helper_plugin_catmenu_namespace
15*aa591c90SLORTET    {
16*aa591c90SLORTET        if ($this->nsHelper === null) {
17*aa591c90SLORTET            $this->nsHelper = $this->loadHelper('catmenu_namespace');
18*aa591c90SLORTET        }
19*aa591c90SLORTET        return $this->nsHelper;
20*aa591c90SLORTET    }
21*aa591c90SLORTET
226983cdfdSLORTET    public function register(Doku_Event_Handler $controller)
236983cdfdSLORTET    {
246983cdfdSLORTET        $controller->register_hook('DOKUWIKI_STARTED',       'AFTER',  $this, 'addJsInfo');
256983cdfdSLORTET        $controller->register_hook('PROSEMIRROR_RENDER_PLUGIN', 'BEFORE', $this, 'handleRender');
266983cdfdSLORTET        $controller->register_hook('PROSEMIRROR_PARSE_UNKNOWN', 'BEFORE', $this, 'handleParseUnknown');
276983cdfdSLORTET        $controller->register_hook('PLUGIN_PAGESICON_UPDATED',  'AFTER',  $this, 'handlePagesiconUpdated');
286983cdfdSLORTET    }
296983cdfdSLORTET
30*aa591c90SLORTET    /**
31*aa591c90SLORTET     * Vérifie si une page contenant du catmenu est affectée par la mise à jour d'une icône.
32*aa591c90SLORTET     * Retourne true si au moins un bloc catmenu de la page couvre la page cible.
33*aa591c90SLORTET     */
346983cdfdSLORTET    private function pageUsesAffectedCatmenu(string $hostPageID, string $content, string $targetPage): bool
356983cdfdSLORTET    {
366983cdfdSLORTET        if (!preg_match_all('/\{\{catmenu>(.*?)\}\}/i', $content, $matches, PREG_SET_ORDER)) return false;
37*aa591c90SLORTET        $nsHelper = $this->getNsHelper();
386983cdfdSLORTET        foreach ($matches as $match) {
396983cdfdSLORTET            $namespaceExpr = (string)($match[1] ?? '');
40*aa591c90SLORTET            $resolvedNS    = $nsHelper->resolveNamespaceExpression($namespaceExpr, $hostPageID);
41*aa591c90SLORTET            if ($nsHelper->isTargetInNamespace($targetPage, $resolvedNS)) {
426983cdfdSLORTET                return true;
436983cdfdSLORTET            }
446983cdfdSLORTET        }
456983cdfdSLORTET        return false;
466983cdfdSLORTET    }
476983cdfdSLORTET
48*aa591c90SLORTET    /**
49*aa591c90SLORTET     * Invalide les caches des pages contenant un bloc catmenu affecté.
50*aa591c90SLORTET     *
51*aa591c90SLORTET     * Si $targetPage est fourni, seules les pages dont le catmenu couvre réellement
52*aa591c90SLORTET     * cette page cible sont invalidées. Sinon, toutes les pages contenant catmenu
53*aa591c90SLORTET     * sont invalidées (fallback large).
54*aa591c90SLORTET     */
55*aa591c90SLORTET    private function invalidateCacheForTarget(string $targetPage = ''): void
566983cdfdSLORTET    {
576983cdfdSLORTET        global $conf;
586983cdfdSLORTET        $datadir = rtrim((string)$conf['datadir'], '/');
596983cdfdSLORTET        if ($datadir === '' || !is_dir($datadir)) return;
606983cdfdSLORTET
616983cdfdSLORTET        $it = new RecursiveIteratorIterator(
626983cdfdSLORTET            new RecursiveDirectoryIterator($datadir, FilesystemIterator::SKIP_DOTS)
636983cdfdSLORTET        );
646983cdfdSLORTET        foreach ($it as $fileinfo) {
656983cdfdSLORTET            /** @var SplFileInfo $fileinfo */
666983cdfdSLORTET            if (!$fileinfo->isFile()) continue;
676983cdfdSLORTET            if (substr($fileinfo->getFilename(), -4) !== '.txt') continue;
686983cdfdSLORTET
696983cdfdSLORTET            $path    = $fileinfo->getPathname();
706983cdfdSLORTET            $content = @file_get_contents($path);
71*aa591c90SLORTET            // Pré-filtre rapide : ignorer les pages sans aucun bloc catmenu
72*aa591c90SLORTET            if ($content === false || strpos($content, '{{catmenu>') === false) continue;
736983cdfdSLORTET
746983cdfdSLORTET            $id = pathID($path);
756983cdfdSLORTET            if ($id === '') continue;
76*aa591c90SLORTET
77*aa591c90SLORTET            // Filtre précis : n'invalider que si le namespace catmenu couvre réellement la cible
78*aa591c90SLORTET            if ($targetPage !== '' && !$this->pageUsesAffectedCatmenu($id, $content, $targetPage)) {
79*aa591c90SLORTET                continue;
80*aa591c90SLORTET            }
81*aa591c90SLORTET
826983cdfdSLORTET            $cache = new \dokuwiki\Cache\CacheRenderer($id, wikiFN($id), 'xhtml');
836983cdfdSLORTET            $cache->removeCache();
846983cdfdSLORTET        }
856983cdfdSLORTET    }
866983cdfdSLORTET
876983cdfdSLORTET    public function addJsInfo(Doku_Event $event)
886983cdfdSLORTET    {
896983cdfdSLORTET        global $ID;
906983cdfdSLORTET        global $JSINFO;
916983cdfdSLORTET        if (!isset($JSINFO['plugins'])) $JSINFO['plugins'] = [];
926983cdfdSLORTET        if (!isset($JSINFO['plugins']['catmenu'])) $JSINFO['plugins']['catmenu'] = [];
936983cdfdSLORTET        $JSINFO['plugins']['catmenu']['show_in_editor_menu'] = (bool)$this->getConf('show_in_editor_menu');
946983cdfdSLORTET
95*aa591c90SLORTET        // Actions activées dans le menu contextuel (issues de la config multicheckbox)
96*aa591c90SLORTET        $rawItems = (string)$this->getConf('context_menu_items');
97*aa591c90SLORTET        $JSINFO['plugins']['catmenu']['context_menu_items'] = array_values(
98*aa591c90SLORTET            array_filter(array_map('trim', explode(',', $rawItems)))
99*aa591c90SLORTET        );
100*aa591c90SLORTET
1016983cdfdSLORTET        $pagesiconHelper = plugin_load('helper', 'pagesicon');
1026983cdfdSLORTET        $JSINFO['plugins']['catmenu']['pagesicon_available'] = (bool)$pagesiconHelper;
1036983cdfdSLORTET        if ($pagesiconHelper) {
1046983cdfdSLORTET            $JSINFO['plugins']['catmenu']['pagesicon_upload_url'] = wl((string)$ID, ['do' => 'pagesicon']);
1056983cdfdSLORTET        }
1066983cdfdSLORTET    }
1076983cdfdSLORTET
1086983cdfdSLORTET    public function handleRender(Doku_Event $event)
1096983cdfdSLORTET    {
1106983cdfdSLORTET        $data = $event->data;
1116983cdfdSLORTET        if (($data['name'] ?? '') !== 'catmenu_catmenu') return;
1126983cdfdSLORTET
1136983cdfdSLORTET        $event->preventDefault();
1146983cdfdSLORTET        $event->stopPropagation();
1156983cdfdSLORTET
1166983cdfdSLORTET        $syntax = trim((string)($data['match'] ?? ''));
1176983cdfdSLORTET        if ($syntax === '') {
1186983cdfdSLORTET            $syntax = '{{catmenu>.}}';
1196983cdfdSLORTET        }
1206983cdfdSLORTET
1216983cdfdSLORTET        $node = new Node('dwplugin_block');
1226983cdfdSLORTET        $node->attr('class', 'dwplugin');
1236983cdfdSLORTET        $node->attr('data-pluginname', 'catmenu');
1246983cdfdSLORTET
1256983cdfdSLORTET        $textNode = new Node('text');
1266983cdfdSLORTET        $textNode->setText($syntax);
1276983cdfdSLORTET        $node->addChild($textNode);
1286983cdfdSLORTET
1296983cdfdSLORTET        $data['renderer']->addToNodestack($node);
1306983cdfdSLORTET    }
1316983cdfdSLORTET
1326983cdfdSLORTET    public function handleParseUnknown(Doku_Event $event)
1336983cdfdSLORTET    {
1346983cdfdSLORTET        if (($event->data['node']['type'] ?? '') !== 'catmenu') return;
1356983cdfdSLORTET
1366983cdfdSLORTET        $event->data['newNode'] = new CatmenuNode($event->data['node'], $event->data['parent']);
1376983cdfdSLORTET        $event->preventDefault();
1386983cdfdSLORTET        $event->stopPropagation();
1396983cdfdSLORTET    }
1406983cdfdSLORTET
1416983cdfdSLORTET    public function handlePagesiconUpdated(Doku_Event $event): void
1426983cdfdSLORTET    {
143*aa591c90SLORTET        // Récupération de la page cible depuis les données de l'événement pagesicon.
144*aa591c90SLORTET        // Si la donnée n'est pas disponible, on invalide de façon large (toutes les pages catmenu).
145*aa591c90SLORTET        $targetPage = (string)($event->data['page'] ?? $event->data['id'] ?? '');
146*aa591c90SLORTET        $this->invalidateCacheForTarget($targetPage);
1476983cdfdSLORTET    }
1486983cdfdSLORTET}
149