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 434f2a4045SGerrit Uitslag $act = $INPUT->post->str('comment'); 444f2a4045SGerrit Uitslag if ($act && !checkSecurityToken()) { 454f2a4045SGerrit Uitslag return; 464f2a4045SGerrit Uitslag } 474f2a4045SGerrit 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); 944f2a4045SGerrit 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(); 974f2a4045SGerrit Uitslag ptln('</div>', 10); // class="no" 984f2a4045SGerrit Uitslag ptln('</form>', 8); 994f2a4045SGerrit 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 { 172*ad918f72SGerrit Uitslag global $lang; 173*ad918f72SGerrit Uitslag 174573e23a1Swikidesign $id = $thread['id']; 175573e23a1Swikidesign 176c3413364SGerrit Uitslag $labels = [ 177573e23a1Swikidesign 0 => $this->getLang('off'), 178573e23a1Swikidesign 1 => $this->getLang('open'), 179573e23a1Swikidesign 2 => $this->getLang('closed') 180c3413364SGerrit Uitslag ]; 1816fd05bf5Swikidesign $title = p_get_metadata($id, 'title'); 1824cded5e1SGerrit Uitslag if (!$title) { 1834cded5e1SGerrit Uitslag $title = $id; 1844cded5e1SGerrit Uitslag } 185*ad918f72SGerrit Uitslag $align = $lang['direction'] === 'rtl' ? 'left' : 'right'; 1864f2a4045SGerrit Uitslag echo '<h2 name="' . $id . '" id="' . $id . '">' . hsc($title) . '</h2>' 1874f2a4045SGerrit Uitslag . '<form method="post" action="' . wl($id) . '">' 188*ad918f72SGerrit Uitslag . '<div class="media' . $align . '">' 1894f2a4045SGerrit Uitslag . '<input type="hidden" name="do" value="admin" />' 1904f2a4045SGerrit Uitslag . '<input type="hidden" name="page" value="discussion" />' 1914f2a4045SGerrit Uitslag . '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />' 1924f2a4045SGerrit Uitslag . $this->getLang('status') . ': ' 1934f2a4045SGerrit Uitslag . '<select name="status" size="1">'; 194573e23a1Swikidesign foreach ($labels as $key => $label) { 195c3413364SGerrit Uitslag $selected = ($key == $thread['status'] ? ' selected="selected"' : ''); 1964f2a4045SGerrit Uitslag echo '<option value="' . $key . '"' . $selected . '>' . $label . '</option>'; 197573e23a1Swikidesign } 1984f2a4045SGerrit Uitslag echo '</select> ' 1994f2a4045SGerrit Uitslag . '<input type="submit" class="button" name="comment" value="' . $this->getLang('btn_change') . '" ' 2004f2a4045SGerrit Uitslag . 'title="' . $this->getLang('btn_change') . '" />' 2014f2a4045SGerrit Uitslag . '</div>' 2024f2a4045SGerrit Uitslag . '</form>' 2034f2a4045SGerrit Uitslag . '<div class="level2">' 2044f2a4045SGerrit Uitslag . '<a href="' . wl($id) . '" class="wikilink1">' . $id . '</a> '; 205573e23a1Swikidesign return true; 206573e23a1Swikidesign } 207573e23a1Swikidesign 208573e23a1Swikidesign /** 209573e23a1Swikidesign * Returns the full comments data for a given wiki page 210e7ac9adaSGerrit Uitslag * 211c3413364SGerrit Uitslag * @param array $thread by reference with: 2124f2a4045SGerrit Uitslag * 'id' => string page id, 213c3413364SGerrit Uitslag * 'file' => string file location of .comments metadata file 214c3413364SGerrit Uitslag * 'status' => int 215c3413364SGerrit Uitslag * 'number' => int number of visible comments 216c3413364SGerrit Uitslag * 217e7ac9adaSGerrit Uitslag * @return array|bool 218573e23a1Swikidesign */ 2199699b3e2SGerrit Uitslag protected function getComments(&$thread) 2209699b3e2SGerrit Uitslag { 221573e23a1Swikidesign $id = $thread['id']; 222573e23a1Swikidesign 2234cded5e1SGerrit Uitslag if (!$thread['file']) { 2244cded5e1SGerrit Uitslag $thread['file'] = metaFN($id, '.comments'); 2254cded5e1SGerrit Uitslag } 226573e23a1Swikidesign if (!@file_exists($thread['file'])) return false; // no discussion thread at all 227573e23a1Swikidesign 228573e23a1Swikidesign $data = unserialize(io_readFile($thread['file'], false)); 229573e23a1Swikidesign 230573e23a1Swikidesign $thread['status'] = $data['status']; 231573e23a1Swikidesign $thread['number'] = $data['number']; 23276fdd2cdSGerrit Uitslag if (empty($data['status'])) return false; // comments are turned off 23376fdd2cdSGerrit Uitslag if (empty($data['comments'])) return false; // no comments 234573e23a1Swikidesign 235c3413364SGerrit Uitslag $result = []; 236573e23a1Swikidesign foreach ($data['comments'] as $cid => $comment) { 2374f2a4045SGerrit Uitslag $this->addComment($cid, $data, $result, $id); 238573e23a1Swikidesign } 239573e23a1Swikidesign 2404cded5e1SGerrit Uitslag if (empty($result)) { 2414cded5e1SGerrit Uitslag return false; 2424cded5e1SGerrit Uitslag } else { 2434cded5e1SGerrit Uitslag return $result; 2444cded5e1SGerrit Uitslag } 245573e23a1Swikidesign } 246573e23a1Swikidesign 247573e23a1Swikidesign /** 248573e23a1Swikidesign * Recursive function to add the comment hierarchy to the result 249e7ac9adaSGerrit Uitslag * 250c3413364SGerrit Uitslag * @param string $cid comment id of current comment 251c3413364SGerrit Uitslag * @param array $data array with all comments by reference 252c3413364SGerrit Uitslag * @param array $result array with all comments by reference enhanced with level 2534f2a4045SGerrit Uitslag * @param string $id page id 254c3413364SGerrit Uitslag * @param string $parent comment id of parent or empty 255c3413364SGerrit Uitslag * @param int $level level of current comment, higher is deeper 256573e23a1Swikidesign */ 2574f2a4045SGerrit Uitslag protected function addComment($cid, &$data, &$result, $id, $parent = '', $level = 1) 2589699b3e2SGerrit Uitslag { 25976fdd2cdSGerrit Uitslag if (!isset($data['comments'][$cid]) || !is_array($data['comments'][$cid])) return; // corrupt datatype 260c3413364SGerrit Uitslag 261573e23a1Swikidesign $comment = $data['comments'][$cid]; 262c3413364SGerrit Uitslag // handle only replies to given parent comment 263c3413364SGerrit Uitslag if ($comment['parent'] != $parent) return; 264573e23a1Swikidesign 265573e23a1Swikidesign // okay, add the comment to the result 2664f2a4045SGerrit Uitslag $comment['id'] = $id; 267573e23a1Swikidesign $comment['level'] = $level; 268573e23a1Swikidesign $result[] = $comment; 269573e23a1Swikidesign 270573e23a1Swikidesign // check answers to this comment 271573e23a1Swikidesign if (count($comment['replies'])) { 272573e23a1Swikidesign foreach ($comment['replies'] as $rid) { 2734f2a4045SGerrit Uitslag $this->addComment($rid, $data, $result, $id, $cid, $level + 1); 274573e23a1Swikidesign } 275573e23a1Swikidesign } 276264b7327Swikidesign } 277264b7327Swikidesign 278264b7327Swikidesign /** 279c3413364SGerrit Uitslag * Returns html of checkbox and info about a comment item 280e7ac9adaSGerrit Uitslag * 281c3413364SGerrit Uitslag * @param array $comment array with comment data 282c3413364SGerrit Uitslag * @return string html of checkbox and info 283264b7327Swikidesign */ 2849699b3e2SGerrit Uitslag public function commentItem($comment) 2859699b3e2SGerrit Uitslag { 286264b7327Swikidesign global $conf; 287264b7327Swikidesign 288264b7327Swikidesign // prepare variables 289264b7327Swikidesign if (is_array($comment['user'])) { // new format 290264b7327Swikidesign $name = $comment['user']['name']; 291264b7327Swikidesign $mail = $comment['user']['mail']; 292264b7327Swikidesign } else { // old format 293264b7327Swikidesign $name = $comment['name']; 294264b7327Swikidesign $mail = $comment['mail']; 295264b7327Swikidesign } 296264b7327Swikidesign if (is_array($comment['date'])) { // new format 297264b7327Swikidesign $created = $comment['date']['created']; 298264b7327Swikidesign } else { // old format 299264b7327Swikidesign $created = $comment['date']; 300264b7327Swikidesign } 301573e23a1Swikidesign $abstract = preg_replace('/\s+?/', ' ', strip_tags($comment['xhtml'])); 302c3413364SGerrit Uitslag if (PhpString::strlen($abstract) > 160) { 303c3413364SGerrit Uitslag $abstract = PhpString::substr($abstract, 0, 160) . '...'; 3044cded5e1SGerrit Uitslag } 305264b7327Swikidesign 3064f2a4045SGerrit Uitslag return '<input type="checkbox" name="cid[' . $comment['cid'] . ']" value="1" /> ' 3074f2a4045SGerrit Uitslag . $this->email($mail, $name, 'email') 3084f2a4045SGerrit Uitslag . ', <a href="' . wl($comment['id']) . '#comment_' . $comment['cid'] . '" class="wikilink1">' 3094f2a4045SGerrit Uitslag . strftime($conf['dformat'], $created) . ': ' 3104f2a4045SGerrit Uitslag . '</a>' 3114f2a4045SGerrit Uitslag . '<span class="abstract">' . $abstract . '</span>'; 312264b7327Swikidesign } 313264b7327Swikidesign 314264b7327Swikidesign /** 315c3413364SGerrit Uitslag * Returns html of list item openings tag 316e7ac9adaSGerrit Uitslag * 317e7ac9adaSGerrit Uitslag * @param array $comment 318e7ac9adaSGerrit Uitslag * @return string 319264b7327Swikidesign */ 3209699b3e2SGerrit Uitslag public function liComment($comment) 3219699b3e2SGerrit Uitslag { 322c3413364SGerrit Uitslag $showclass = ($comment['show'] ? '' : ' hidden'); 323c3413364SGerrit Uitslag return '<li class="level' . $comment['level'] . $showclass . '">'; 324264b7327Swikidesign } 325264b7327Swikidesign 326264b7327Swikidesign /** 327264b7327Swikidesign * Show buttons to bulk remove, hide or show comments 328264b7327Swikidesign */ 3299699b3e2SGerrit Uitslag protected function actionButtons() 3309699b3e2SGerrit Uitslag { 331264b7327Swikidesign global $lang; 332264b7327Swikidesign 3336fd05bf5Swikidesign ptln('<div class="comment_buttons">', 12); 3346fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="' . $this->getLang('btn_show') . '" class="button" title="' . $this->getLang('btn_show') . '" />', 14); 3356fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="' . $this->getLang('btn_hide') . '" class="button" title="' . $this->getLang('btn_hide') . '" />', 14); 3366fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="' . $lang['btn_delete'] . '" class="button" title="' . $lang['btn_delete'] . '" />', 14); 3376fd05bf5Swikidesign ptln('</div>', 12); // class="comment_buttons" 338573e23a1Swikidesign } 339573e23a1Swikidesign 340573e23a1Swikidesign /** 341573e23a1Swikidesign * Displays links to older newer discussions 342e7ac9adaSGerrit Uitslag * 343c3413364SGerrit Uitslag * @param bool $isMore whether there are more pages needed 344c3413364SGerrit Uitslag * @param int $first first entry on this page 345c3413364SGerrit Uitslag * @param int $num number of entries per page 346573e23a1Swikidesign */ 3479699b3e2SGerrit Uitslag protected function browseDiscussionLinks($isMore, $first, $num) 3489699b3e2SGerrit Uitslag { 349573e23a1Swikidesign global $ID; 350573e23a1Swikidesign 351c3413364SGerrit Uitslag if ($first == 0 && !$isMore) return; 3523e02b3ffSwikidesign 353c3413364SGerrit Uitslag $params = ['do' => 'admin', 'page' => 'discussion']; 354573e23a1Swikidesign $last = $first + $num; 3556fd05bf5Swikidesign ptln('<div class="level1">', 8); 356c3413364SGerrit Uitslag $return = ''; 357573e23a1Swikidesign if ($first > 0) { 358573e23a1Swikidesign $first -= $num; 3594cded5e1SGerrit Uitslag if ($first < 0) { 3604cded5e1SGerrit Uitslag $first = 0; 3614cded5e1SGerrit Uitslag } 362573e23a1Swikidesign $params['first'] = $first; 3636fd05bf5Swikidesign ptln('<p class="centeralign">', 8); 364c3413364SGerrit Uitslag $return = '<a href="' . wl($ID, $params) . '" class="wikilink1"><< ' . $this->getLang('newer') . '</a>'; 365c3413364SGerrit Uitslag if ($isMore) { 366c3413364SGerrit Uitslag $return .= ' | '; 367573e23a1Swikidesign } else { 368c3413364SGerrit Uitslag ptln($return, 10); 3696fd05bf5Swikidesign ptln('</p>', 8); 370573e23a1Swikidesign } 371c3413364SGerrit Uitslag } elseif ($isMore) { 3726fd05bf5Swikidesign ptln('<p class="centeralign">', 8); 373573e23a1Swikidesign } 374c3413364SGerrit Uitslag if ($isMore) { 375573e23a1Swikidesign $params['first'] = $last; 376c3413364SGerrit Uitslag $return .= '<a href="' . wl($ID, $params) . '" class="wikilink1">' . $this->getLang('older') . ' >></a>'; 377c3413364SGerrit Uitslag ptln($return, 10); 3786fd05bf5Swikidesign ptln('</p>', 8); 379573e23a1Swikidesign } 3806fd05bf5Swikidesign ptln('</div>', 6); // class="level1" 381573e23a1Swikidesign } 382573e23a1Swikidesign 383573e23a1Swikidesign /** 384c3413364SGerrit Uitslag * Changes the status of a comment section 385e7ac9adaSGerrit Uitslag * 386c3413364SGerrit Uitslag * @param int $new 0=disabled, 1=enabled, 2=closed 387573e23a1Swikidesign */ 3889699b3e2SGerrit Uitslag protected function changeStatus($new) 3899699b3e2SGerrit Uitslag { 390573e23a1Swikidesign global $ID; 391573e23a1Swikidesign 392573e23a1Swikidesign // get discussion meta file name 393573e23a1Swikidesign $file = metaFN($ID, '.comments'); 394573e23a1Swikidesign $data = unserialize(io_readFile($file, false)); 395573e23a1Swikidesign 396573e23a1Swikidesign $old = $data['status']; 397c3413364SGerrit Uitslag if ($old == $new) { 398c3413364SGerrit Uitslag return; 399c3413364SGerrit Uitslag } 400573e23a1Swikidesign 401573e23a1Swikidesign // save the comment metadata file 402573e23a1Swikidesign $data['status'] = $new; 403573e23a1Swikidesign io_saveFile($file, serialize($data)); 404573e23a1Swikidesign 405573e23a1Swikidesign // look for ~~DISCUSSION~~ command in page file and change it accordingly 406c3413364SGerrit Uitslag $patterns = ['~~DISCUSSION:off\2~~', '~~DISCUSSION\2~~', '~~DISCUSSION:closed\2~~']; 407573e23a1Swikidesign $replace = $patterns[$new]; 4083e02b3ffSwikidesign $wiki = preg_replace('/~~DISCUSSION([\w:]*)(\|?.*?)~~/', $replace, rawWiki($ID)); 409573e23a1Swikidesign saveWikiText($ID, $wiki, $this->getLang('statuschanged'), true); 410264b7327Swikidesign } 4115ef1705fSiLoveiDo} 412