163314d65SLeo Eibler<?php 263314d65SLeo Eibler 363314d65SLeo Eibler/** 463314d65SLeo Eibler * ToDo Action Plugin: Inserts button for ToDo plugin into toolbar 563314d65SLeo Eibler * 663314d65SLeo Eibler * Original Example: http://www.dokuwiki.org/devel:action_plugins 763314d65SLeo Eibler * @author Babbage <babbage@digitalbrink.com> 816a9e697SLeo Eibler * @date 20130405 Leo Eibler <dokuwiki@sprossenwanne.at> \n 916a9e697SLeo Eibler * replace old sack() method with new jQuery method and use post instead of get \n 104f445722SLeo Eibler * @date 20130408 Leo Eibler <dokuwiki@sprossenwanne.at> \n 11aa11d6a4Sleibler * remove getInfo() call because it's done by plugin.info.txt (since dokuwiki 2009-12-25 Lemming) 1263314d65SLeo Eibler */ 1363314d65SLeo Eibler 1463314d65SLeo Eiblerif(!defined('DOKU_INC')) die(); 1563314d65SLeo Eibler/** 16aa11d6a4Sleibler * Class action_plugin_todo registers actions 1763314d65SLeo Eibler */ 18aa11d6a4Sleiblerclass action_plugin_todo extends DokuWiki_Action_Plugin { 1963314d65SLeo Eibler 2063314d65SLeo Eibler /** 2163314d65SLeo Eibler * Register the eventhandlers 2263314d65SLeo Eibler */ 23aa11d6a4Sleibler public function register(Doku_Event_Handler $controller) { 2463314d65SLeo Eibler $controller->register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'insert_button', array()); 25aa11d6a4Sleibler $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, '_ajax_call', array()); 2663314d65SLeo Eibler } 2763314d65SLeo Eibler 2863314d65SLeo Eibler /** 2963314d65SLeo Eibler * Inserts the toolbar button 3063314d65SLeo Eibler */ 31aa11d6a4Sleibler public function insert_button(&$event, $param) { 3263314d65SLeo Eibler $event->data[] = array( 3363314d65SLeo Eibler 'type' => 'format', 3463314d65SLeo Eibler 'title' => $this->getLang('qb_todobutton'), 3563314d65SLeo Eibler 'icon' => '../../plugins/todo/todo.png', 3663314d65SLeo Eibler 'key' => 't', 3763314d65SLeo Eibler 'open' => '<todo>', 3863314d65SLeo Eibler 'close' => '</todo>', 3916a9e697SLeo Eibler 'block' => false, 4063314d65SLeo Eibler ); 4163314d65SLeo Eibler } 4263314d65SLeo Eibler 43aa11d6a4Sleibler /** 44aa11d6a4Sleibler * Handles ajax requests for to do plugin 45aa11d6a4Sleibler * 46aa11d6a4Sleibler * @brief This method is called by ajax if the user clicks on the to-do checkbox or the to-do text. 47aa11d6a4Sleibler * It sets the to-do state to completed or reset it to open. 48aa11d6a4Sleibler * 49aa11d6a4Sleibler * POST Parameters: 50aa11d6a4Sleibler * index int the position of the occurrence of the input element (starting with 0 for first element/to-do) 51aa11d6a4Sleibler * checked int should the to-do set to completed (1) or to open (0) 52aa11d6a4Sleibler * path string id/path/name of the page 53aa11d6a4Sleibler * 54aa11d6a4Sleibler * @date 20140317 Leo Eibler <dokuwiki@sprossenwanne.at> \n 55aa11d6a4Sleibler * use todo content as change description \n 56aa11d6a4Sleibler * @date 20131008 Gerrit Uitslag <klapinklapin@gmail.com> \n 57aa11d6a4Sleibler * move ajax.php to action.php, added lock and conflict checks and improved saving 58aa11d6a4Sleibler * @date 20130405 Leo Eibler <dokuwiki@sprossenwanne.at> \n 59aa11d6a4Sleibler * replace old sack() method with new jQuery method and use post instead of get \n 60aa11d6a4Sleibler * @date 20130407 Leo Eibler <dokuwiki@sprossenwanne.at> \n 61aa11d6a4Sleibler * add user assignment for todos \n 62aa11d6a4Sleibler * @date 20130408 Christian Marg <marg@rz.tu-clausthal.de> \n 63aa11d6a4Sleibler * change only the clicked to-do item instead of all items with the same text \n 64aa11d6a4Sleibler * origVal is not used anymore, we use the index (occurrence) of input element \n 65aa11d6a4Sleibler * @date 20130408 Leo Eibler <dokuwiki@sprossenwanne.at> \n 66aa11d6a4Sleibler * migrate changes made by Christian Marg to current version of plugin \n 67aa11d6a4Sleibler * 68aa11d6a4Sleibler * 69aa11d6a4Sleibler * @param Doku_Event $event 70aa11d6a4Sleibler * @param mixed $param not defined 71aa11d6a4Sleibler */ 72aa11d6a4Sleibler public function _ajax_call(&$event, $param) { 73aa11d6a4Sleibler global $ID, $conf, $lang; 74aa11d6a4Sleibler 75aa11d6a4Sleibler if($event->data !== 'plugin_todo') { 76aa11d6a4Sleibler return; 77aa11d6a4Sleibler } 78aa11d6a4Sleibler //no other ajax call handlers needed 79aa11d6a4Sleibler $event->stopPropagation(); 80aa11d6a4Sleibler $event->preventDefault(); 81aa11d6a4Sleibler 82aa11d6a4Sleibler #Variables 83aa11d6a4Sleibler // by einhirn <marg@rz.tu-clausthal.de> determine checkbox index by using class 'todocheckbox' 84aa11d6a4Sleibler 85aa11d6a4Sleibler if(isset($_REQUEST['index'], $_REQUEST['checked'], $_REQUEST['pageid'])) { 86aa11d6a4Sleibler // index = position of occurrence of <input> element (starting with 0 for first element) 87aa11d6a4Sleibler $index = (int) $_REQUEST['index']; 88aa11d6a4Sleibler // checked = flag if input is checked means to do is complete (1) or not (0) 89aa11d6a4Sleibler $checked = (boolean) urldecode($_REQUEST['checked']); 90aa11d6a4Sleibler // path = page ID 91aa11d6a4Sleibler $ID = cleanID(urldecode($_REQUEST['pageid'])); 92aa11d6a4Sleibler } else { 93aa11d6a4Sleibler return; 94aa11d6a4Sleibler } 95aa11d6a4Sleibler 96aa11d6a4Sleibler $date = 0; 97aa11d6a4Sleibler if(isset($_REQUEST['date'])) $date = (int) $_REQUEST['date']; 98aa11d6a4Sleibler 99aa11d6a4Sleibler $INFO = pageinfo(); 100aa11d6a4Sleibler 101aa11d6a4Sleibler #Determine Permissions 102aa11d6a4Sleibler if(auth_quickaclcheck($ID) < AUTH_EDIT) { 103aa11d6a4Sleibler echo "You do not have permission to edit this file.\nAccess was denied."; 104aa11d6a4Sleibler return; 105aa11d6a4Sleibler } 106aa11d6a4Sleibler // Check, if page is locked 107aa11d6a4Sleibler if(checklock($ID)) { 108aa11d6a4Sleibler $locktime = filemtime(wikiLockFN($ID)); 109aa11d6a4Sleibler $expire = dformat($locktime + $conf['locktime']); 110aa11d6a4Sleibler $min = round(($conf['locktime'] - (time() - $locktime)) / 60); 111aa11d6a4Sleibler 112aa11d6a4Sleibler $msg = $this->getLang('lockedpage').' 113aa11d6a4Sleibler'.$lang['lockedby'] . ': ' . editorinfo($INFO['locked']) . ' 114aa11d6a4Sleibler' . $lang['lockexpire'] . ': ' . $expire . ' (' . $min . ' min)'; 115aa11d6a4Sleibler $this->printJson(array('message' => $msg)); 116aa11d6a4Sleibler return; 117aa11d6a4Sleibler } 118aa11d6a4Sleibler 119aa11d6a4Sleibler //conflict check 120aa11d6a4Sleibler if($date != 0 && $INFO['meta']['date']['modified'] > $date) { 121aa11d6a4Sleibler $this->printJson(array('message' => $this->getLang('refreshpage'))); 122aa11d6a4Sleibler return; 123aa11d6a4Sleibler } 124aa11d6a4Sleibler 125aa11d6a4Sleibler #Retrieve Page Contents 126aa11d6a4Sleibler $wikitext = rawWiki($ID); 127aa11d6a4Sleibler 128aa11d6a4Sleibler #Determine position of tag 129aa11d6a4Sleibler if($index >= 0) { 130aa11d6a4Sleibler $index++; 131aa11d6a4Sleibler // index is only set on the current page with the todos 132aa11d6a4Sleibler // the occurances are counted, untill the index-th input is reached which is updated 133aa11d6a4Sleibler $todoTagStartPos = $this->_strnpos($wikitext, '<todo', $index); 134aa11d6a4Sleibler $todoTagEndPos = strpos($wikitext, '>', $todoTagStartPos) + 1; 135aa11d6a4Sleibler 136aa11d6a4Sleibler if($todoTagEndPos > $todoTagStartPos) { 137aa11d6a4Sleibler // @date 20140714 le add todo text to minorchange 138aa11d6a4Sleibler $todoTextEndPos = strpos( $wikitext, '</todo', $todoTagEndPos ); 139aa11d6a4Sleibler $todoText = substr( $wikitext, $todoTagEndPos, $todoTextEndPos-$todoTagEndPos ); 140aa11d6a4Sleibler // update text 141aa11d6a4Sleibler $oldTag = substr($wikitext, $todoTagStartPos, ($todoTagEndPos - $todoTagStartPos)); 142aa11d6a4Sleibler $newTag = $this->_buildTodoTag($oldTag, $checked); 143aa11d6a4Sleibler $wikitext = substr_replace($wikitext, $newTag, $todoTagStartPos, ($todoTagEndPos - $todoTagStartPos)); 144aa11d6a4Sleibler 145aa11d6a4Sleibler // save Update (Minor) 146aa11d6a4Sleibler lock($ID); 147aa11d6a4Sleibler // @date 20140714 le add todo text to minorchange, use different message for checked or unchecked 148aa11d6a4Sleibler saveWikiText($ID, $wikitext, $this->getLang($checked?'checkboxchange_on':'checkboxchange_off').': '.$todoText, $minoredit = true); 149aa11d6a4Sleibler unlock($ID); 150aa11d6a4Sleibler 151aa11d6a4Sleibler $return = array( 152aa11d6a4Sleibler 'date' => @filemtime(wikiFN($ID)), 153aa11d6a4Sleibler 'succeed' => true 154aa11d6a4Sleibler ); 155aa11d6a4Sleibler $this->printJson($return); 156aa11d6a4Sleibler } 157aa11d6a4Sleibler } 158aa11d6a4Sleibler } 159aa11d6a4Sleibler 160aa11d6a4Sleibler /** 161aa11d6a4Sleibler * Encode and print an arbitrary variable into JSON format 162aa11d6a4Sleibler * 163aa11d6a4Sleibler * @param mixed $return 164aa11d6a4Sleibler */ 165aa11d6a4Sleibler private function printJson($return) { 166aa11d6a4Sleibler $json = new JSON(); 167aa11d6a4Sleibler echo $json->encode($return); 168aa11d6a4Sleibler } 169aa11d6a4Sleibler 170aa11d6a4Sleibler /** 171aa11d6a4Sleibler * @brief gets current to-do tag and returns a new one depending on checked 172aa11d6a4Sleibler * @param $todoTag string current to-do tag e.g. <todo @user> 173aa11d6a4Sleibler * @param $checked int check flag (todo completed=1, todo uncompleted=0) 174aa11d6a4Sleibler * @return string new to-do completed or uncompleted tag e.g. <todo @user #> 175aa11d6a4Sleibler */ 176aa11d6a4Sleibler private function _buildTodoTag($todoTag, $checked) { 177*bd2f9c37Srunout-at $user = ''; 178c34903f3Srunout-at if($checked == 1) { 179*bd2f9c37Srunout-at if(!empty($_SERVER['REMOTE_USER'])) { $user = $_SERVER['REMOTE_USER']; } 180*bd2f9c37Srunout-at $newTag = preg_replace('/>/', ' #'.$user.':'.date('Y-m-d').'>', $todoTag); 181c34903f3Srunout-at } else { 182*bd2f9c37Srunout-at $newTag = preg_replace('/[\s]*[#].*>/', '>', $todoTag); 183c34903f3Srunout-at } 184aa11d6a4Sleibler return $newTag; 185aa11d6a4Sleibler } 186aa11d6a4Sleibler 187*bd2f9c37Srunout-at 188aa11d6a4Sleibler /** 189aa11d6a4Sleibler * Find position of $occurance-th $needle in haystack 190aa11d6a4Sleibler */ 191aa11d6a4Sleibler private function _strnpos($haystack, $needle, $occurance, $pos = 0) { 192aa11d6a4Sleibler for($i = 1; $i <= $occurance; $i++) { 193aa11d6a4Sleibler $pos = strpos($haystack, $needle, $pos) + 1; 194aa11d6a4Sleibler } 195aa11d6a4Sleibler return $pos - 1; 196aa11d6a4Sleibler } 19763314d65SLeo Eibler} 198