1<?php
2/**
3 * MoaiEditor plugin for DokuWiki
4 */
5# Constants
6const MOAIED_FAKE_MESSAGES = false;     # Create fake messages (info, error, success, notify) to test template compatibilty
7
8#include_once('/var/www/html/zdebug/inc.php');
9
10use dokuwiki\Extension\Plugin;
11
12if(!defined('DOKU_INC')) die();
13
14class action_plugin_moaieditor extends DokuWiki_Action_Plugin {
15
16    // Register hook function
17    public function register(Doku_Event_Handler $controller) {
18        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'moaieditor_main');
19        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'moaieditor_ajax');
20    }
21
22    // Handle ajax calls
23    public function moaieditor_ajax (Doku_Event &$event, $param) {
24
25        # Boilerplate for handling ajax calls
26        if ($event->data !== 'moaieditor_preview') return;
27        $event->stopPropagation();
28        $event->preventDefault();
29
30        # Check for needed keys
31        foreach (['request_id','page_id','text'] as $k) {
32            if (!in_array($k, array_keys($_REQUEST))) {
33                http_status(403);
34                echo 'Invalid request';
35                exit;
36            }
37        }
38        # Unpack
39        $request_id = $_REQUEST['request_id'];
40        $page_id    = $_REQUEST['page_id'];
41        $text       = $_REQUEST['text'];
42        global $ID;
43        $ID = $page_id;
44
45        # Specific check for logged users (must have the correct token)
46        if (!checkSecurityToken()) {
47            echo 'CRSF Attack';
48            return;
49        }
50        # Check if the user has permission to edit this page
51        if (auth_quickaclcheck($ID) < AUTH_EDIT) {
52            http_status(403);
53            echo 'Forbidden';
54            exit;
55        }
56        # Default header, ajax call may overwrite it later
57        header('Content-Type: text/html; charset=utf-8');
58
59        # Render the content
60        $info = [];
61        $html = p_render('xhtml', p_get_instructions($text), $info);
62        echo $request_id.$html;
63    }
64
65    // Main function
66    public function moaieditor_main (Doku_Event &$event, $param) {
67        global $ACT, $INFO, $conf, $lang;
68
69        // Exit if not in edit mode
70        if (!in_array($ACT, ['edit'])) {
71            return;
72        }
73        // Load the JS files in 'templates/' and 'user_templates/'
74        $info = $this->getInfo();
75        $version = str_replace('-', '', $info['date']);
76        $plugin_path = DOKU_PLUGIN.'moaieditor';
77        $plugin_url  = DOKU_BASE.'lib/plugins/moaieditor';
78        $t = $this->scanTemplateDir ($plugin_url, $plugin_path, "templates");
79        $u = $this->scanTemplateDir ($plugin_url, $plugin_path, "user_templates");
80        $templates = array_merge($t, $u);
81        foreach ($templates as $template)
82            $event->data['script'][] = array(
83                'type' => 'text/javascript',
84                'charset' => 'utf-8',
85                'src' => $plugin_url.'/'.$template['folder'].'/'.$template['name'].".js?v=$version",
86                'defer' => 'defer',
87            );
88        // Pass some data to Javascript
89        $jsinfo = array(
90            // Configuration options
91            'base_url'                  => $plugin_url,
92            'templates'                 => $templates,
93            'hide_editor_by_default'    => $this->getConf('hide_editor_by_default'),
94            'editor_width_side_by_side' => $this->getConf('editor_width_side_by_side'),
95            'editor_width_top_bottom'   => $this->getConf('editor_width_top_bottom'),
96            'editor_width_single_pane'  => $this->getConf('editor_width_single_pane'),
97            // Docinfo
98            'docinfo'                   => $this->getDocinfo(),
99            // Editor intro message
100            'intromsg'                  => $this->getEditorIntroMessage(),
101        );
102        $event->data['script'][] = array(
103            'type' => 'text/javascript',
104            '_data' => 'JSINFO.plugin_moaieditor = ' . json_encode($jsinfo),
105        );
106        // Simulate template messages (for testing template compatibility)
107        if( isset($_GET['fakemsg']) || MOAIED_FAKE_MESSAGES)
108            $this->simulate_template_messages();
109    }
110
111    // Scan template dir
112    public function scanTemplateDir ($plugin_url, $plugin_path, $folder) {
113        $files = [];
114        foreach (scandir("$plugin_path/$folder") as $filename)
115            if (str_ends_with($filename, '.js'))
116                if (!str_ends_with($filename, 'default.js')) {
117                    $name = pathinfo($filename, PATHINFO_FILENAME);
118                    $css = Null;
119                    if (file_exists("$plugin_path/$folder/$name.css"))
120                        $css = "$plugin_url/$folder/$name.css";
121                    $files[] = ['name'=>$name, 'folder'=>$folder, 'css'=>$css];
122                }
123        return $files;
124    }
125
126    // Get the intro message shown by DokuWiki in editor mode (whether we show it or not will depend on user settings)
127    public function getEditorIntroMessage () {
128        # The editor intro message is rendered here: inc/Ui/Editor.php -> show()
129        # In editor mode, it can be of two types: 'editrev' or 'edit'
130        #      /inc/lang/en/edit.txt       "**You've loaded an old revision of the document!** If you save it, you will create a new version with this data."
131        #      /inc/lang/en/editrev.txt    "Edit the page and hit ''Save''. See [[wiki:syntax]] for Wiki syntax. Please edit the page only if you can **improve** it. If you want to test some things, learn to make your first steps on the [[playground:playground|playground]]."
132        global $REV;
133        global $INFO;
134        $wr = $INFO['writable'] && !$INFO['locked'];
135        if (!$wr)
136            return null;
137        $type = ($REV) ? 'editrev' : 'edit';   // intro locale text (edit, rditrev, or read)
138        $html = p_locale_xhtml($type);
139        return ['type'=>$type, 'html'=>$html];
140    }
141
142    // Get document info (mostly copied from 'tpl_pageinfo' but we want to format the data differently)
143    public function getDocinfo () {
144        global $conf, $lang, $INFO, $ID;
145
146        // Document info will not exist when creating a new page
147        if (!$INFO['exists'])
148            return null;
149
150        // Document path on disk
151        $fn = $INFO['filepath'];
152        if (!$conf['fullpath']) {
153            if ($INFO['rev'])
154                $fn = str_replace($conf['olddir'] . '/', '', $fn);
155            else
156                $fn = str_replace($conf['datadir'] . '/', '', $fn);
157        }
158        $fn = utf8_decodeFN($fn);
159
160        // Last modification time
161        $dateLocal = dformat($INFO['lastmod']);
162        $dateIso = date(DATE_ISO8601, $INFO['lastmod']);
163
164        // Edited by
165        if ($INFO['editor'])
166            $by = $lang['by'].' <bdi><b>'.editorinfo($INFO['editor']).'</b></bdi>';
167        else
168            $by = '('.$lang['external_edit'].')';
169
170        // Return it
171        return [
172            'labelPath' => 'Document path on disk',
173            'labelMod'  => $lang['lastmod'],
174            'path'      => "<bdi><i>data/pages/</i><b>$fn</b></bdi>",
175            'time'      => "<time datetime='$dateIso'><b>$dateLocal</b></time>",
176            'by'        => $by
177        ];
178    }
179
180    // Create fake messages to test template compatibility
181    public function simulate_template_messages () {
182        global $MSG;
183        $MSG[] = ['msg'=>'Error message (-1)',  'lvl'=>'error',   'allow'=>0, 'line'=>'', 'file'=>''];
184        $MSG[] = ['msg'=>'Info message (0)',    'lvl'=>'info',    'allow'=>0, 'line'=>'', 'file'=>''];
185        $MSG[] = ['msg'=>'Success message (1)', 'lvl'=>'success', 'allow'=>0, 'line'=>'', 'file'=>''];
186        $MSG[] = ['msg'=>'Notify message (2)',  'lvl'=>'notify',  'allow'=>0, 'line'=>'', 'file'=>''];
187    }
188
189} // End class
190