1a768ba62Swikidesign<?php 2264b7327Swikidesign/** 3264b7327Swikidesign * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 4264b7327Swikidesign * @author Esther Brunner <wikidesign@gmail.com> 5264b7327Swikidesign */ 6264b7327Swikidesign 7c3413364SGerrit Uitslaguse dokuwiki\Utf8\PhpString; 8c3413364SGerrit Uitslag 9de7e6f00SGerrit Uitslag/** 10de7e6f00SGerrit Uitslag * Class admin_plugin_discussion 11de7e6f00SGerrit Uitslag */ 129699b3e2SGerrit Uitslagclass admin_plugin_discussion extends DokuWiki_Admin_Plugin 139699b3e2SGerrit Uitslag{ 14a768ba62Swikidesign 15de7e6f00SGerrit Uitslag /** 16de7e6f00SGerrit Uitslag * @return int 17de7e6f00SGerrit Uitslag */ 189699b3e2SGerrit Uitslag public function getMenuSort() 199699b3e2SGerrit Uitslag { 209699b3e2SGerrit Uitslag return 200; 219699b3e2SGerrit Uitslag } 22de7e6f00SGerrit Uitslag 23de7e6f00SGerrit Uitslag /** 24de7e6f00SGerrit Uitslag * @return bool 25de7e6f00SGerrit Uitslag */ 269699b3e2SGerrit Uitslag public function forAdminOnly() 279699b3e2SGerrit Uitslag { 289699b3e2SGerrit Uitslag return false; 299699b3e2SGerrit Uitslag } 30a768ba62Swikidesign 319699b3e2SGerrit Uitslag public function handle() 329699b3e2SGerrit Uitslag { 33c3413364SGerrit Uitslag global $lang, $INPUT; 34573e23a1Swikidesign 35c3413364SGerrit Uitslag $cids = $INPUT->post->arr('cid'); 36c3413364SGerrit Uitslag if (is_array($cids)) { 37c3413364SGerrit Uitslag $cids = array_keys($cids); 38c3413364SGerrit Uitslag } 39de7e6f00SGerrit Uitslag /** @var action_plugin_discussion $action */ 40c3413364SGerrit Uitslag $action = plugin_load('action', 'discussion'); 41573e23a1Swikidesign if (!$action) return; // couldn't load action plugin component 42573e23a1Swikidesign 43*4f2a4045SGerrit Uitslag $act = $INPUT->post->str('comment'); 44*4f2a4045SGerrit Uitslag if ($act && !checkSecurityToken()) { 45*4f2a4045SGerrit Uitslag return; 46*4f2a4045SGerrit Uitslag } 47*4f2a4045SGerrit Uitslag switch ($act) { 48573e23a1Swikidesign case $lang['btn_delete']: 49c3413364SGerrit Uitslag $action->save($cids, ''); 50573e23a1Swikidesign break; 51573e23a1Swikidesign 52573e23a1Swikidesign case $this->getLang('btn_show'): 53c3413364SGerrit Uitslag $action->save($cids, '', 'show'); 54573e23a1Swikidesign break; 55573e23a1Swikidesign 56573e23a1Swikidesign case $this->getLang('btn_hide'): 57c3413364SGerrit Uitslag $action->save($cids, '', 'hide'); 58573e23a1Swikidesign break; 59573e23a1Swikidesign 60573e23a1Swikidesign case $this->getLang('btn_change'): 61c3413364SGerrit Uitslag $this->changeStatus($INPUT->post->str('status')); 62573e23a1Swikidesign break; 63573e23a1Swikidesign } 64573e23a1Swikidesign } 65573e23a1Swikidesign 669699b3e2SGerrit Uitslag public function html() 679699b3e2SGerrit Uitslag { 68c3413364SGerrit Uitslag global $conf, $INPUT; 69264b7327Swikidesign 70c3413364SGerrit Uitslag $first = $INPUT->int('first'); 71c3413364SGerrit Uitslag 72c3413364SGerrit Uitslag $num = $conf['recent'] ?: 20; 73264b7327Swikidesign 74573e23a1Swikidesign ptln('<h1>' . $this->getLang('menu') . '</h1>'); 75264b7327Swikidesign 76c3413364SGerrit Uitslag $threads = $this->getThreads(); 77573e23a1Swikidesign 78573e23a1Swikidesign // slice the needed chunk of discussion pages 79c3413364SGerrit Uitslag $isMore = count($threads) > ($first + $num); 80573e23a1Swikidesign $threads = array_slice($threads, $first, $num); 81573e23a1Swikidesign 82264b7327Swikidesign foreach ($threads as $thread) { 83c3413364SGerrit Uitslag $comments = $this->getComments($thread); 84c3413364SGerrit Uitslag $this->threadHead($thread); 85573e23a1Swikidesign if ($comments === false) { 866fd05bf5Swikidesign ptln('</div>', 6); // class="level2" 87264b7327Swikidesign continue; 88264b7327Swikidesign } 89264b7327Swikidesign 906fd05bf5Swikidesign ptln('<form method="post" action="' . wl($thread['id']) . '">', 8); 916fd05bf5Swikidesign ptln('<div class="no">', 10); 926fd05bf5Swikidesign ptln('<input type="hidden" name="do" value="admin" />', 10); 936fd05bf5Swikidesign ptln('<input type="hidden" name="page" value="discussion" />', 10); 94*4f2a4045SGerrit Uitslag ptln('<input type="hidden" name="sectok" value="'.getSecurityToken().'" />', 10); 95c3413364SGerrit Uitslag echo html_buildlist($comments, 'admin_discussion', [$this, 'commentItem'], [$this, 'liComment']); 96c3413364SGerrit Uitslag $this->actionButtons(); 97*4f2a4045SGerrit Uitslag ptln('</div>', 10); // class="no" 98*4f2a4045SGerrit Uitslag ptln('</form>', 8); 99*4f2a4045SGerrit Uitslag ptln('</div>', 6); // class="level2" 100264b7327Swikidesign } 101c3413364SGerrit Uitslag $this->browseDiscussionLinks($isMore, $first, $num); 1026fd05bf5Swikidesign 103264b7327Swikidesign } 104264b7327Swikidesign 105264b7327Swikidesign /** 106573e23a1Swikidesign * Returns an array of pages with discussion sections, sorted by recent comments 107e7ac9adaSGerrit Uitslag * 108e7ac9adaSGerrit Uitslag * @return array 109573e23a1Swikidesign */ 1109699b3e2SGerrit Uitslag protected function getThreads() 1119699b3e2SGerrit Uitslag { 112573e23a1Swikidesign global $conf; 113573e23a1Swikidesign 114573e23a1Swikidesign // returns the list of pages in the given namespace and it's subspaces 115c3413364SGerrit Uitslag $items = []; 116c3413364SGerrit Uitslag search($items, $conf['datadir'], 'search_allpages', []); 117573e23a1Swikidesign 118573e23a1Swikidesign // add pages with comments to result 119c3413364SGerrit Uitslag $result = []; 120573e23a1Swikidesign foreach ($items as $item) { 121573e23a1Swikidesign $id = $item['id']; 122573e23a1Swikidesign 123573e23a1Swikidesign // some checks 124573e23a1Swikidesign $file = metaFN($id, '.comments'); 125573e23a1Swikidesign if (!@file_exists($file)) continue; // skip if no comments file 126573e23a1Swikidesign 127573e23a1Swikidesign $date = filemtime($file); 128c3413364SGerrit Uitslag $result[] = [ 129573e23a1Swikidesign 'id' => $id, 130573e23a1Swikidesign 'file' => $file, 131573e23a1Swikidesign 'date' => $date, 132c3413364SGerrit Uitslag ]; 133573e23a1Swikidesign } 134573e23a1Swikidesign 135573e23a1Swikidesign // finally sort by time of last comment 136c3413364SGerrit Uitslag usort($result, ['admin_plugin_discussion', 'threadCmp']); 137573e23a1Swikidesign 138573e23a1Swikidesign return $result; 139573e23a1Swikidesign } 140573e23a1Swikidesign 141573e23a1Swikidesign /** 14271a0ae95SGina Haeussge * Callback for comparison of thread data. 14371a0ae95SGina Haeussge * 14471a0ae95SGina Haeussge * Used for sorting threads in descending order by date of last comment. 14571a0ae95SGina Haeussge * If this date happens to be equal for the compared threads, page id 14671a0ae95SGina Haeussge * is used as second comparison attribute. 147e7ac9adaSGerrit Uitslag * 148e7ac9adaSGerrit Uitslag * @param array $a 149e7ac9adaSGerrit Uitslag * @param array $b 150e7ac9adaSGerrit Uitslag * @return int 15171a0ae95SGina Haeussge */ 1529699b3e2SGerrit Uitslag protected function threadCmp($a, $b) 1539699b3e2SGerrit Uitslag { 15471a0ae95SGina Haeussge if ($a['date'] == $b['date']) { 15571a0ae95SGina Haeussge return strcmp($a['id'], $b['id']); 15671a0ae95SGina Haeussge } 157c3413364SGerrit Uitslag if ($a['date'] < $b['date']) { 158c3413364SGerrit Uitslag return 1; 159c3413364SGerrit Uitslag } else { 160c3413364SGerrit Uitslag return -1; 161c3413364SGerrit Uitslag } 16271a0ae95SGina Haeussge } 16371a0ae95SGina Haeussge 16471a0ae95SGina Haeussge /** 165573e23a1Swikidesign * Outputs header, page ID and status of a discussion thread 166e7ac9adaSGerrit Uitslag * 167e7ac9adaSGerrit Uitslag * @param array $thread 168e7ac9adaSGerrit Uitslag * @return bool 169264b7327Swikidesign */ 1709699b3e2SGerrit Uitslag protected function threadHead($thread) 1719699b3e2SGerrit Uitslag { 172573e23a1Swikidesign $id = $thread['id']; 173573e23a1Swikidesign 174c3413364SGerrit Uitslag $labels = [ 175573e23a1Swikidesign 0 => $this->getLang('off'), 176573e23a1Swikidesign 1 => $this->getLang('open'), 177573e23a1Swikidesign 2 => $this->getLang('closed') 178c3413364SGerrit Uitslag ]; 1796fd05bf5Swikidesign $title = p_get_metadata($id, 'title'); 1804cded5e1SGerrit Uitslag if (!$title) { 1814cded5e1SGerrit Uitslag $title = $id; 1824cded5e1SGerrit Uitslag } 183*4f2a4045SGerrit Uitslag echo '<h2 name="' . $id . '" id="' . $id . '">' . hsc($title) . '</h2>' 184*4f2a4045SGerrit Uitslag . '<form method="post" action="' . wl($id) . '">' 185*4f2a4045SGerrit Uitslag . '<div class="mediaright">' 186*4f2a4045SGerrit Uitslag . '<input type="hidden" name="do" value="admin" />' 187*4f2a4045SGerrit Uitslag . '<input type="hidden" name="page" value="discussion" />' 188*4f2a4045SGerrit Uitslag . '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />' 189*4f2a4045SGerrit Uitslag . $this->getLang('status') . ': ' 190*4f2a4045SGerrit Uitslag . '<select name="status" size="1">'; 191573e23a1Swikidesign foreach ($labels as $key => $label) { 192c3413364SGerrit Uitslag $selected = ($key == $thread['status'] ? ' selected="selected"' : ''); 193*4f2a4045SGerrit Uitslag echo '<option value="' . $key . '"' . $selected . '>' . $label . '</option>'; 194573e23a1Swikidesign } 195*4f2a4045SGerrit Uitslag echo '</select> ' 196*4f2a4045SGerrit Uitslag . '<input type="submit" class="button" name="comment" value="' . $this->getLang('btn_change') . '" ' 197*4f2a4045SGerrit Uitslag . 'title="' . $this->getLang('btn_change') . '" />' 198*4f2a4045SGerrit Uitslag . '</div>' 199*4f2a4045SGerrit Uitslag . '</form>' 200*4f2a4045SGerrit Uitslag . '<div class="level2">' 201*4f2a4045SGerrit Uitslag . '<a href="' . wl($id) . '" class="wikilink1">' . $id . '</a> '; 202573e23a1Swikidesign return true; 203573e23a1Swikidesign } 204573e23a1Swikidesign 205573e23a1Swikidesign /** 206573e23a1Swikidesign * Returns the full comments data for a given wiki page 207e7ac9adaSGerrit Uitslag * 208c3413364SGerrit Uitslag * @param array $thread by reference with: 209*4f2a4045SGerrit Uitslag * 'id' => string page id, 210c3413364SGerrit Uitslag * 'file' => string file location of .comments metadata file 211c3413364SGerrit Uitslag * 'status' => int 212c3413364SGerrit Uitslag * 'number' => int number of visible comments 213c3413364SGerrit Uitslag * 214e7ac9adaSGerrit Uitslag * @return array|bool 215573e23a1Swikidesign */ 2169699b3e2SGerrit Uitslag protected function getComments(&$thread) 2179699b3e2SGerrit Uitslag { 218573e23a1Swikidesign $id = $thread['id']; 219573e23a1Swikidesign 2204cded5e1SGerrit Uitslag if (!$thread['file']) { 2214cded5e1SGerrit Uitslag $thread['file'] = metaFN($id, '.comments'); 2224cded5e1SGerrit Uitslag } 223573e23a1Swikidesign if (!@file_exists($thread['file'])) return false; // no discussion thread at all 224573e23a1Swikidesign 225573e23a1Swikidesign $data = unserialize(io_readFile($thread['file'], false)); 226573e23a1Swikidesign 227573e23a1Swikidesign $thread['status'] = $data['status']; 228573e23a1Swikidesign $thread['number'] = $data['number']; 22976fdd2cdSGerrit Uitslag if (empty($data['status'])) return false; // comments are turned off 23076fdd2cdSGerrit Uitslag if (empty($data['comments'])) return false; // no comments 231573e23a1Swikidesign 232c3413364SGerrit Uitslag $result = []; 233573e23a1Swikidesign foreach ($data['comments'] as $cid => $comment) { 234*4f2a4045SGerrit Uitslag $this->addComment($cid, $data, $result, $id); 235573e23a1Swikidesign } 236573e23a1Swikidesign 2374cded5e1SGerrit Uitslag if (empty($result)) { 2384cded5e1SGerrit Uitslag return false; 2394cded5e1SGerrit Uitslag } else { 2404cded5e1SGerrit Uitslag return $result; 2414cded5e1SGerrit Uitslag } 242573e23a1Swikidesign } 243573e23a1Swikidesign 244573e23a1Swikidesign /** 245573e23a1Swikidesign * Recursive function to add the comment hierarchy to the result 246e7ac9adaSGerrit Uitslag * 247c3413364SGerrit Uitslag * @param string $cid comment id of current comment 248c3413364SGerrit Uitslag * @param array $data array with all comments by reference 249c3413364SGerrit Uitslag * @param array $result array with all comments by reference enhanced with level 250*4f2a4045SGerrit Uitslag * @param string $id page id 251c3413364SGerrit Uitslag * @param string $parent comment id of parent or empty 252c3413364SGerrit Uitslag * @param int $level level of current comment, higher is deeper 253573e23a1Swikidesign */ 254*4f2a4045SGerrit Uitslag protected function addComment($cid, &$data, &$result, $id, $parent = '', $level = 1) 2559699b3e2SGerrit Uitslag { 25676fdd2cdSGerrit Uitslag if (!isset($data['comments'][$cid]) || !is_array($data['comments'][$cid])) return; // corrupt datatype 257c3413364SGerrit Uitslag 258573e23a1Swikidesign $comment = $data['comments'][$cid]; 259c3413364SGerrit Uitslag // handle only replies to given parent comment 260c3413364SGerrit Uitslag if ($comment['parent'] != $parent) return; 261573e23a1Swikidesign 262573e23a1Swikidesign // okay, add the comment to the result 263*4f2a4045SGerrit Uitslag $comment['id'] = $id; 264573e23a1Swikidesign $comment['level'] = $level; 265573e23a1Swikidesign $result[] = $comment; 266573e23a1Swikidesign 267573e23a1Swikidesign // check answers to this comment 268573e23a1Swikidesign if (count($comment['replies'])) { 269573e23a1Swikidesign foreach ($comment['replies'] as $rid) { 270*4f2a4045SGerrit Uitslag $this->addComment($rid, $data, $result, $id, $cid, $level + 1); 271573e23a1Swikidesign } 272573e23a1Swikidesign } 273264b7327Swikidesign } 274264b7327Swikidesign 275264b7327Swikidesign /** 276c3413364SGerrit Uitslag * Returns html of checkbox and info about a comment item 277e7ac9adaSGerrit Uitslag * 278c3413364SGerrit Uitslag * @param array $comment array with comment data 279c3413364SGerrit Uitslag * @return string html of checkbox and info 280264b7327Swikidesign */ 2819699b3e2SGerrit Uitslag public function commentItem($comment) 2829699b3e2SGerrit Uitslag { 283264b7327Swikidesign global $conf; 284264b7327Swikidesign 285264b7327Swikidesign // prepare variables 286264b7327Swikidesign if (is_array($comment['user'])) { // new format 287264b7327Swikidesign $name = $comment['user']['name']; 288264b7327Swikidesign $mail = $comment['user']['mail']; 289264b7327Swikidesign } else { // old format 290264b7327Swikidesign $name = $comment['name']; 291264b7327Swikidesign $mail = $comment['mail']; 292264b7327Swikidesign } 293264b7327Swikidesign if (is_array($comment['date'])) { // new format 294264b7327Swikidesign $created = $comment['date']['created']; 295264b7327Swikidesign } else { // old format 296264b7327Swikidesign $created = $comment['date']; 297264b7327Swikidesign } 298573e23a1Swikidesign $abstract = preg_replace('/\s+?/', ' ', strip_tags($comment['xhtml'])); 299c3413364SGerrit Uitslag if (PhpString::strlen($abstract) > 160) { 300c3413364SGerrit Uitslag $abstract = PhpString::substr($abstract, 0, 160) . '...'; 3014cded5e1SGerrit Uitslag } 302264b7327Swikidesign 303*4f2a4045SGerrit Uitslag return '<input type="checkbox" name="cid[' . $comment['cid'] . ']" value="1" /> ' 304*4f2a4045SGerrit Uitslag . $this->email($mail, $name, 'email') 305*4f2a4045SGerrit Uitslag . ', <a href="' . wl($comment['id']) . '#comment_' . $comment['cid'] . '" class="wikilink1">' 306*4f2a4045SGerrit Uitslag . strftime($conf['dformat'], $created) . ': ' 307*4f2a4045SGerrit Uitslag . '</a>' 308*4f2a4045SGerrit Uitslag . '<span class="abstract">' . $abstract . '</span>'; 309264b7327Swikidesign } 310264b7327Swikidesign 311264b7327Swikidesign /** 312c3413364SGerrit Uitslag * Returns html of list item openings tag 313e7ac9adaSGerrit Uitslag * 314e7ac9adaSGerrit Uitslag * @param array $comment 315e7ac9adaSGerrit Uitslag * @return string 316264b7327Swikidesign */ 3179699b3e2SGerrit Uitslag public function liComment($comment) 3189699b3e2SGerrit Uitslag { 319c3413364SGerrit Uitslag $showclass = ($comment['show'] ? '' : ' hidden'); 320c3413364SGerrit Uitslag return '<li class="level' . $comment['level'] . $showclass . '">'; 321264b7327Swikidesign } 322264b7327Swikidesign 323264b7327Swikidesign /** 324264b7327Swikidesign * Show buttons to bulk remove, hide or show comments 325264b7327Swikidesign */ 3269699b3e2SGerrit Uitslag protected function actionButtons() 3279699b3e2SGerrit Uitslag { 328264b7327Swikidesign global $lang; 329264b7327Swikidesign 3306fd05bf5Swikidesign ptln('<div class="comment_buttons">', 12); 3316fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="' . $this->getLang('btn_show') . '" class="button" title="' . $this->getLang('btn_show') . '" />', 14); 3326fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="' . $this->getLang('btn_hide') . '" class="button" title="' . $this->getLang('btn_hide') . '" />', 14); 3336fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="' . $lang['btn_delete'] . '" class="button" title="' . $lang['btn_delete'] . '" />', 14); 3346fd05bf5Swikidesign ptln('</div>', 12); // class="comment_buttons" 335573e23a1Swikidesign } 336573e23a1Swikidesign 337573e23a1Swikidesign /** 338573e23a1Swikidesign * Displays links to older newer discussions 339e7ac9adaSGerrit Uitslag * 340c3413364SGerrit Uitslag * @param bool $isMore whether there are more pages needed 341c3413364SGerrit Uitslag * @param int $first first entry on this page 342c3413364SGerrit Uitslag * @param int $num number of entries per page 343573e23a1Swikidesign */ 3449699b3e2SGerrit Uitslag protected function browseDiscussionLinks($isMore, $first, $num) 3459699b3e2SGerrit Uitslag { 346573e23a1Swikidesign global $ID; 347573e23a1Swikidesign 348c3413364SGerrit Uitslag if ($first == 0 && !$isMore) return; 3493e02b3ffSwikidesign 350c3413364SGerrit Uitslag $params = ['do' => 'admin', 'page' => 'discussion']; 351573e23a1Swikidesign $last = $first + $num; 3526fd05bf5Swikidesign ptln('<div class="level1">', 8); 353c3413364SGerrit Uitslag $return = ''; 354573e23a1Swikidesign if ($first > 0) { 355573e23a1Swikidesign $first -= $num; 3564cded5e1SGerrit Uitslag if ($first < 0) { 3574cded5e1SGerrit Uitslag $first = 0; 3584cded5e1SGerrit Uitslag } 359573e23a1Swikidesign $params['first'] = $first; 3606fd05bf5Swikidesign ptln('<p class="centeralign">', 8); 361c3413364SGerrit Uitslag $return = '<a href="' . wl($ID, $params) . '" class="wikilink1"><< ' . $this->getLang('newer') . '</a>'; 362c3413364SGerrit Uitslag if ($isMore) { 363c3413364SGerrit Uitslag $return .= ' | '; 364573e23a1Swikidesign } else { 365c3413364SGerrit Uitslag ptln($return, 10); 3666fd05bf5Swikidesign ptln('</p>', 8); 367573e23a1Swikidesign } 368c3413364SGerrit Uitslag } elseif ($isMore) { 3696fd05bf5Swikidesign ptln('<p class="centeralign">', 8); 370573e23a1Swikidesign } 371c3413364SGerrit Uitslag if ($isMore) { 372573e23a1Swikidesign $params['first'] = $last; 373c3413364SGerrit Uitslag $return .= '<a href="' . wl($ID, $params) . '" class="wikilink1">' . $this->getLang('older') . ' >></a>'; 374c3413364SGerrit Uitslag ptln($return, 10); 3756fd05bf5Swikidesign ptln('</p>', 8); 376573e23a1Swikidesign } 3776fd05bf5Swikidesign ptln('</div>', 6); // class="level1" 378573e23a1Swikidesign } 379573e23a1Swikidesign 380573e23a1Swikidesign /** 381c3413364SGerrit Uitslag * Changes the status of a comment section 382e7ac9adaSGerrit Uitslag * 383c3413364SGerrit Uitslag * @param int $new 0=disabled, 1=enabled, 2=closed 384573e23a1Swikidesign */ 3859699b3e2SGerrit Uitslag protected function changeStatus($new) 3869699b3e2SGerrit Uitslag { 387573e23a1Swikidesign global $ID; 388573e23a1Swikidesign 389573e23a1Swikidesign // get discussion meta file name 390573e23a1Swikidesign $file = metaFN($ID, '.comments'); 391573e23a1Swikidesign $data = unserialize(io_readFile($file, false)); 392573e23a1Swikidesign 393573e23a1Swikidesign $old = $data['status']; 394c3413364SGerrit Uitslag if ($old == $new) { 395c3413364SGerrit Uitslag return; 396c3413364SGerrit Uitslag } 397573e23a1Swikidesign 398573e23a1Swikidesign // save the comment metadata file 399573e23a1Swikidesign $data['status'] = $new; 400573e23a1Swikidesign io_saveFile($file, serialize($data)); 401573e23a1Swikidesign 402573e23a1Swikidesign // look for ~~DISCUSSION~~ command in page file and change it accordingly 403c3413364SGerrit Uitslag $patterns = ['~~DISCUSSION:off\2~~', '~~DISCUSSION\2~~', '~~DISCUSSION:closed\2~~']; 404573e23a1Swikidesign $replace = $patterns[$new]; 4053e02b3ffSwikidesign $wiki = preg_replace('/~~DISCUSSION([\w:]*)(\|?.*?)~~/', $replace, rawWiki($ID)); 406573e23a1Swikidesign saveWikiText($ID, $wiki, $this->getLang('statuschanged'), true); 407264b7327Swikidesign } 4085ef1705fSiLoveiDo} 409