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