xref: /plugin/discussion/admin.php (revision 573e23a1826b1683fa668aede546befbffcfcb10)
1a768ba62Swikidesign<?php
2264b7327Swikidesign/**
3264b7327Swikidesign * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4264b7327Swikidesign * @author     Esther Brunner <wikidesign@gmail.com>
5264b7327Swikidesign */
6264b7327Swikidesign
7264b7327Swikidesign// must be run within Dokuwiki
8264b7327Swikidesignif (!defined('DOKU_INC')) die();
9264b7327Swikidesign
10a768ba62Swikidesignif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
11264b7327Swikidesign
12a768ba62Swikidesignrequire_once(DOKU_PLUGIN.'admin.php');
13a768ba62Swikidesign
14a768ba62Swikidesignclass admin_plugin_discussion extends DokuWiki_Admin_Plugin {
15a768ba62Swikidesign
16a768ba62Swikidesign  function getInfo(){
17a768ba62Swikidesign    return array(
18264b7327Swikidesign      'author' => 'Esther Brunner',
19264b7327Swikidesign      'email'  => 'wikidesign@gmail.com',
20*573e23a1Swikidesign      'date'   => '2007-03-01',
21264b7327Swikidesign      'name'   => 'Discussion Plugin (admin component)',
22*573e23a1Swikidesign      'desc'   => 'Moderate discussions',
23264b7327Swikidesign      'url'    => 'http://www.wikidesign.ch/en/plugin/discussion/start',
24a768ba62Swikidesign    );
25a768ba62Swikidesign  }
26a768ba62Swikidesign
27264b7327Swikidesign  function getMenuSort(){ return 200; }
28264b7327Swikidesign  function forAdminOnly(){ return false; }
29a768ba62Swikidesign
30*573e23a1Swikidesign  function handle(){
31*573e23a1Swikidesign    global $lang;
32*573e23a1Swikidesign
33*573e23a1Swikidesign    $cid = $_REQUEST['cid'];
34*573e23a1Swikidesign    if (is_array($cid)) $cid = array_keys($cid);
35*573e23a1Swikidesign
36*573e23a1Swikidesign    $action =& plugin_load('action', 'discussion');
37*573e23a1Swikidesign    if (!$action) return; // couldn't load action plugin component
38*573e23a1Swikidesign
39*573e23a1Swikidesign    switch ($_REQUEST['comment']){
40*573e23a1Swikidesign      case $lang['btn_delete']:
41*573e23a1Swikidesign        $action->_save($cid, '');
42*573e23a1Swikidesign        break;
43*573e23a1Swikidesign
44*573e23a1Swikidesign      case $this->getLang('btn_show'):
45*573e23a1Swikidesign        $action->_save($cid, '', 'show');
46*573e23a1Swikidesign        break;
47*573e23a1Swikidesign
48*573e23a1Swikidesign      case $this->getLang('btn_hide'):
49*573e23a1Swikidesign        $action->_save($cid, '', 'hide');
50*573e23a1Swikidesign        break;
51*573e23a1Swikidesign
52*573e23a1Swikidesign      case $this->getLang('btn_change'):
53*573e23a1Swikidesign        $new = $_REQUEST['status'];
54*573e23a1Swikidesign        $this->_changeStatus($new);
55*573e23a1Swikidesign        break;
56*573e23a1Swikidesign    }
57*573e23a1Swikidesign  }
58*573e23a1Swikidesign
59a768ba62Swikidesign  function html(){
60264b7327Swikidesign    global $conf;
61264b7327Swikidesign
62*573e23a1Swikidesign    $first = $_REQUEST['first'];
63*573e23a1Swikidesign    if (!is_numeric($first)) $first = 0;
64*573e23a1Swikidesign    $num = $conf['recent'];
65264b7327Swikidesign
66*573e23a1Swikidesign    ptln('<h1>'.$this->getLang('menu').'</h1>');
67264b7327Swikidesign
68*573e23a1Swikidesign    $threads = $this->_getThreads();
69*573e23a1Swikidesign
70*573e23a1Swikidesign    // slice the needed chunk of discussion pages
71*573e23a1Swikidesign    $more = ((count($threads) > ($first + $num)) ? true : false);
72*573e23a1Swikidesign    $threads = array_slice($threads, $first, $num);
73*573e23a1Swikidesign
74264b7327Swikidesign    foreach ($threads as $thread){
75*573e23a1Swikidesign      $comments = $this->_getComments($thread);
76*573e23a1Swikidesign      $this->_threadHead($thread);
77*573e23a1Swikidesign      if ($comments === false){
78*573e23a1Swikidesign        ptln('</div>'); // class="level2"
79264b7327Swikidesign        continue;
80264b7327Swikidesign      }
81264b7327Swikidesign
82*573e23a1Swikidesign      ptln('<form method="post" action="'.wl($thread['id']).'">', 2);
83*573e23a1Swikidesign      ptln('<div class="no">', 4);
84*573e23a1Swikidesign      ptln('<input type="hidden" name="do" value="admin" />', 6);
85*573e23a1Swikidesign      ptln('<input type="hidden" name="page" value="discussion" />', 6);
86264b7327Swikidesign      echo html_buildlist($comments, 'admin_discussion', array($this, '_commentItem'), array($this, '_li_comment'));
87*573e23a1Swikidesign      $this->_actionButtons($thread['id']);
88264b7327Swikidesign    }
89*573e23a1Swikidesign    $this->_browseDiscussionLinks($more, $first, $num);
90264b7327Swikidesign  }
91264b7327Swikidesign
92264b7327Swikidesign  /**
93*573e23a1Swikidesign   * Returns an array of pages with discussion sections, sorted by recent comments
94*573e23a1Swikidesign   */
95*573e23a1Swikidesign  function _getThreads(){
96*573e23a1Swikidesign    global $conf;
97*573e23a1Swikidesign
98*573e23a1Swikidesign    require_once(DOKU_INC.'inc/search.php');
99*573e23a1Swikidesign
100*573e23a1Swikidesign    // returns the list of pages in the given namespace and it's subspaces
101*573e23a1Swikidesign    $items = array();
102*573e23a1Swikidesign    search($items, $conf['datadir'], 'search_allpages', '');
103*573e23a1Swikidesign
104*573e23a1Swikidesign    // add pages with comments to result
105*573e23a1Swikidesign    $result = array();
106*573e23a1Swikidesign    foreach ($items as $item){
107*573e23a1Swikidesign      $id = $item['id'];
108*573e23a1Swikidesign
109*573e23a1Swikidesign      // some checks
110*573e23a1Swikidesign      $file = metaFN($id, '.comments');
111*573e23a1Swikidesign      if (!@file_exists($file)) continue; // skip if no comments file
112*573e23a1Swikidesign
113*573e23a1Swikidesign      $date = filemtime($file);
114*573e23a1Swikidesign      $result[$date] = array(
115*573e23a1Swikidesign        'id'   => $id,
116*573e23a1Swikidesign        'file' => $file,
117*573e23a1Swikidesign        'date' => $date,
118*573e23a1Swikidesign      );
119*573e23a1Swikidesign    }
120*573e23a1Swikidesign
121*573e23a1Swikidesign    // finally sort by time of last comment
122*573e23a1Swikidesign    krsort($result);
123*573e23a1Swikidesign
124*573e23a1Swikidesign    return $result;
125*573e23a1Swikidesign  }
126*573e23a1Swikidesign
127*573e23a1Swikidesign  /**
128*573e23a1Swikidesign   * Outputs header, page ID and status of a discussion thread
129264b7327Swikidesign   */
130264b7327Swikidesign  function _threadHead($thread){
131*573e23a1Swikidesign    $id = $thread['id'];
132*573e23a1Swikidesign
133*573e23a1Swikidesign    $labels = array(
134*573e23a1Swikidesign      0 => $this->getLang('off'),
135*573e23a1Swikidesign      1 => $this->getLang('open'),
136*573e23a1Swikidesign      2 => $this->getLang('closed')
137264b7327Swikidesign    );
138*573e23a1Swikidesign    ptln('<h2 name="'.$id.'" id="'.$id.'">'.hsc(p_get_metadata($id, 'title')).'</h2>');
139*573e23a1Swikidesign    ptln('<form method="post" action="'.wl($id).'">');
140*573e23a1Swikidesign    ptln('<div class="mediaright">', 2);
141*573e23a1Swikidesign    ptln('<input type="hidden" name="do" value="admin" />', 4);
142*573e23a1Swikidesign    ptln('<input type="hidden" name="page" value="discussion" />', 4);
143*573e23a1Swikidesign    ptln($this->getLang('status').': <select name="status" size="1">', 4);
144*573e23a1Swikidesign    foreach ($labels as $key => $label){
145*573e23a1Swikidesign      $selected = (($key == $thread['status']) ? ' selected="selected"' : '');
146*573e23a1Swikidesign      ptln('<option value="'.$key.'"'.$selected.'>'.$label.'</option>', 6);
147*573e23a1Swikidesign    }
148*573e23a1Swikidesign    ptln('</select> ', 4);
149*573e23a1Swikidesign    ptln('<input type="submit" value="'.$this->getLang('btn_change').'" />', 4);
150*573e23a1Swikidesign    ptln('</div>', 2);
151*573e23a1Swikidesign    ptln('</form>');
152*573e23a1Swikidesign    ptln('<div class="level2">');
153*573e23a1Swikidesign    ptln('<a href="'.wl($id).'" class="wikilink1">'.$id.'</a> ', 2);
154*573e23a1Swikidesign    return true;
155*573e23a1Swikidesign  }
156*573e23a1Swikidesign
157*573e23a1Swikidesign  /**
158*573e23a1Swikidesign   * Returns the full comments data for a given wiki page
159*573e23a1Swikidesign   */
160*573e23a1Swikidesign  function _getComments(&$thread){
161*573e23a1Swikidesign    $id = $thread['id'];
162*573e23a1Swikidesign
163*573e23a1Swikidesign    if (!$thread['file']) $thread['file'] = metaFN($id, '.comments');
164*573e23a1Swikidesign    if (!@file_exists($thread['file'])) return false; // no discussion thread at all
165*573e23a1Swikidesign
166*573e23a1Swikidesign    $data = unserialize(io_readFile($thread['file'], false));
167*573e23a1Swikidesign
168*573e23a1Swikidesign    $thread['status'] = $data['status'];
169*573e23a1Swikidesign    $thread['number'] = $data['number'];
170*573e23a1Swikidesign    if (!$data['status']) return false;               // comments are turned off
171*573e23a1Swikidesign    if (!isset($data['comments']) || !$data['number']) return false; // no comments
172*573e23a1Swikidesign
173*573e23a1Swikidesign    $result = array();
174*573e23a1Swikidesign    foreach ($data['comments'] as $cid => $comment){
175*573e23a1Swikidesign      $this->_addComment($cid, $data, $result);
176*573e23a1Swikidesign    }
177*573e23a1Swikidesign
178*573e23a1Swikidesign    return $result;
179*573e23a1Swikidesign  }
180*573e23a1Swikidesign
181*573e23a1Swikidesign  /**
182*573e23a1Swikidesign   * Recursive function to add the comment hierarchy to the result
183*573e23a1Swikidesign   */
184*573e23a1Swikidesign  function _addComment($cid, &$data, &$result, $parent = '', $level = 1){
185*573e23a1Swikidesign    if (!is_array($data['comments'][$cid])) return; // corrupt datatype
186*573e23a1Swikidesign    $comment = $data['comments'][$cid];
187*573e23a1Swikidesign    if ($comment['parent'] != $parent) return;      // answer to another comment
188*573e23a1Swikidesign
189*573e23a1Swikidesign    // okay, add the comment to the result
190*573e23a1Swikidesign    $comment['id'] = $cid;
191*573e23a1Swikidesign    $comment['level'] = $level;
192*573e23a1Swikidesign    $result[] = $comment;
193*573e23a1Swikidesign
194*573e23a1Swikidesign    // check answers to this comment
195*573e23a1Swikidesign    if (count($comment['replies'])){
196*573e23a1Swikidesign      foreach ($comment['replies'] as $rid){
197*573e23a1Swikidesign        $this->_addComment($rid, $data, $result, $cid, $level + 1);
198*573e23a1Swikidesign      }
199*573e23a1Swikidesign    }
200264b7327Swikidesign  }
201264b7327Swikidesign
202264b7327Swikidesign  /**
203264b7327Swikidesign   * Checkbox and info about a comment item
204264b7327Swikidesign   */
205264b7327Swikidesign  function _commentItem($comment){
206264b7327Swikidesign    global $conf;
207264b7327Swikidesign
208264b7327Swikidesign    // prepare variables
209264b7327Swikidesign    if (is_array($comment['user'])){ // new format
210264b7327Swikidesign      $name    = $comment['user']['name'];
211264b7327Swikidesign      $mail    = $comment['user']['mail'];
212264b7327Swikidesign    } else {                         // old format
213264b7327Swikidesign      $name    = $comment['name'];
214264b7327Swikidesign      $mail    = $comment['mail'];
215264b7327Swikidesign    }
216264b7327Swikidesign    if (is_array($comment['date'])){ // new format
217264b7327Swikidesign      $created  = $comment['date']['created'];
218264b7327Swikidesign    } else {                         // old format
219264b7327Swikidesign      $created  = $comment['date'];
220264b7327Swikidesign    }
221*573e23a1Swikidesign    $abstract = preg_replace('/\s+?/', ' ', strip_tags($comment['xhtml']));
222264b7327Swikidesign    if (utf8_strlen($abstract) > 160) $abstract = utf8_substr($abstract, 0, 160).'...';
223264b7327Swikidesign
224*573e23a1Swikidesign    return '<input type="checkbox" name="cid['.$comment['id'].']" value="1" /> '.
225*573e23a1Swikidesign      $this->email($mail, $name, 'email').', '.date($conf['dformat'], $created).': '.
226*573e23a1Swikidesign      '<span class="abstract">'.$abstract.'</span>';
227264b7327Swikidesign  }
228264b7327Swikidesign
229264b7327Swikidesign  /**
230264b7327Swikidesign   * list item tag
231264b7327Swikidesign   */
232264b7327Swikidesign  function _li_comment($comment){
233264b7327Swikidesign    $show = ($comment['show'] ? '' : ' hidden');
234264b7327Swikidesign    return '<li class="level'.$comment['level'].$show.'">';
235264b7327Swikidesign  }
236264b7327Swikidesign
237264b7327Swikidesign  /**
238264b7327Swikidesign   * Show buttons to bulk remove, hide or show comments
239264b7327Swikidesign   */
240264b7327Swikidesign  function _actionButtons($id){
241264b7327Swikidesign    global $lang;
242264b7327Swikidesign
243*573e23a1Swikidesign    ptln('<div class="comment_buttons">', 6);
244*573e23a1Swikidesign    ptln('<input type="submit" name="comment" value="'.$this->getLang('btn_show').'" class="button" title="'.$this->getLang('btn_show').'" />', 8);
245*573e23a1Swikidesign    ptln('<input type="submit" name="comment" value="'.$this->getLang('btn_hide').'" class="button" title="'.$this->getLang('btn_hide').'" />', 8);
246*573e23a1Swikidesign    ptln('<input type="submit" name="comment" value="'.$lang['btn_delete'].'" class="button" title="'.$lang['btn_delete'].'" />', 8);
247*573e23a1Swikidesign    ptln('</div>', 6); // class="comment_buttons"
248*573e23a1Swikidesign    ptln('</div>', 4); // class="no"
249*573e23a1Swikidesign    ptln('</form>', 2);
250*573e23a1Swikidesign    ptln('</div>'); // class="level2"
251*573e23a1Swikidesign    return true;
252*573e23a1Swikidesign  }
253*573e23a1Swikidesign
254*573e23a1Swikidesign  /**
255*573e23a1Swikidesign   * Displays links to older newer discussions
256*573e23a1Swikidesign   */
257*573e23a1Swikidesign  function _browseDiscussionLinks($more, $first, $num){
258*573e23a1Swikidesign    global $ID;
259*573e23a1Swikidesign
260*573e23a1Swikidesign    $params = array('do' => 'admin', 'page' => 'discussion');
261*573e23a1Swikidesign    $last = $first+$num;
262*573e23a1Swikidesign    if ($first > 0){
263*573e23a1Swikidesign      $first -= $num;
264*573e23a1Swikidesign      if ($first < 0) $first = 0;
265*573e23a1Swikidesign      $params['first'] = $first;
266*573e23a1Swikidesign      ptln('<p class="centeralign">', 2);
267*573e23a1Swikidesign      $ret = '<a href="'.wl($ID, $params).'" class="wikilink1">&lt;&lt; '.$this->getLang('newer').'</a>';
268*573e23a1Swikidesign      if ($more){
269*573e23a1Swikidesign        $ret .= ' | ';
270*573e23a1Swikidesign      } else {
271*573e23a1Swikidesign        ptln($ret, 4);
272*573e23a1Swikidesign        ptln('</p>', 2);
273*573e23a1Swikidesign      }
274*573e23a1Swikidesign    } else if ($more){
275*573e23a1Swikidesign      ptln('<p class="centeralign">', 2);
276*573e23a1Swikidesign    }
277*573e23a1Swikidesign    if ($more){
278*573e23a1Swikidesign      $params['first'] = $last;
279*573e23a1Swikidesign      $ret .= '<a href="'.wl($ID, $params).'" class="wikilink1">'.$this->getLang('older').' &gt;&gt;</a>';
280*573e23a1Swikidesign      ptln($ret, 4);
281*573e23a1Swikidesign      ptln('</p>', 2);
282*573e23a1Swikidesign    }
283*573e23a1Swikidesign    return true;
284*573e23a1Swikidesign  }
285*573e23a1Swikidesign
286*573e23a1Swikidesign  /**
287*573e23a1Swikidesign   * Changes the status of a comment
288*573e23a1Swikidesign   */
289*573e23a1Swikidesign  function _changeStatus($new){
290*573e23a1Swikidesign    global $ID;
291*573e23a1Swikidesign
292*573e23a1Swikidesign    // get discussion meta file name
293*573e23a1Swikidesign    $file = metaFN($ID, '.comments');
294*573e23a1Swikidesign    $data = unserialize(io_readFile($file, false));
295*573e23a1Swikidesign
296*573e23a1Swikidesign    $old = $data['status'];
297*573e23a1Swikidesign    if ($old == $new) return true;
298*573e23a1Swikidesign
299*573e23a1Swikidesign    // save the comment metadata file
300*573e23a1Swikidesign    $data['status'] = $new;
301*573e23a1Swikidesign    io_saveFile($file, serialize($data));
302*573e23a1Swikidesign
303*573e23a1Swikidesign    // look for ~~DISCUSSION~~ command in page file and change it accordingly
304*573e23a1Swikidesign    $patterns = array('~~DISCUSSION:off~~', '~~DISCUSSION~~', '~~DISCUSSION:closed~~');
305*573e23a1Swikidesign    $replace = $patterns[$new];
306*573e23a1Swikidesign    $wiki = preg_replace('/~~DISCUSSION(?:|:off|:closed)~~/', $replace, rawWiki($ID));
307*573e23a1Swikidesign    saveWikiText($ID, $wiki, $this->getLang('statuschanged'), true);
308*573e23a1Swikidesign
309*573e23a1Swikidesign    return true;
310264b7327Swikidesign  }
311264b7327Swikidesign
312264b7327Swikidesign    /**
313264b7327Swikidesign     * function by iDo
314264b7327Swikidesign     */
315264b7327Swikidesign    function _html() {
316434f5172SiLoveiDo    	require_once(DOKU_PLUGIN.'action.php');
317434f5172SiLoveiDo		$actionDiscussion= new action_plugin_discussion();
318434f5172SiLoveiDo
319a768ba62Swikidesign		global $conf;
320434f5172SiLoveiDo		global $INFO;
321434f5172SiLoveiDo		global $ID;
322434f5172SiLoveiDo		global $ADMDISCUSSION;
323434f5172SiLoveiDo
3245ef1705fSiLoveiDo		$oID=$ID;
3255ef1705fSiLoveiDo		$ADMDISCUSSION['page']="adm";
3265ef1705fSiLoveiDo		//Execute action for page
3275ef1705fSiLoveiDo		if (isset($_REQUEST['comment'])) {
3285ef1705fSiLoveiDo			if ($_REQUEST['comment']!='edit') {
3295ef1705fSiLoveiDo
3305ef1705fSiLoveiDo				if (($_REQUEST['comment']=='add') && (isset($_REQUEST['cid']))) {
3315ef1705fSiLoveiDo
3325ef1705fSiLoveiDo				} else {
3335ef1705fSiLoveiDo					$obj=new unusedclass();
3345ef1705fSiLoveiDo					$actionDiscussion->comments($obj, null);
3355ef1705fSiLoveiDo				}
3365ef1705fSiLoveiDo			}
3375ef1705fSiLoveiDo		}
3385ef1705fSiLoveiDo
339a768ba62Swikidesign		$chem=DOKU_INC.$conf['savedir']."/meta/";
340a768ba62Swikidesign		$arr=$this->globr($chem,"*.comments");
341a768ba62Swikidesign		$com =array();
342a768ba62Swikidesign		foreach ($arr as $v) {
343a768ba62Swikidesign			$ap=unserialize(io_readFile($v, false));
344a768ba62Swikidesign			if (isset($ap['comments'])){
345434f5172SiLoveiDo				$ID=substr(str_replace(array($chem,".comments",'/'),array("","",':'),$v),1);
346434f5172SiLoveiDo				$ADMDISCUSSION['page']=' : <a href="'.wl($ID,'').'">'.str_replace("/doku.php/","",wl($ID,'')).'</a>';
347a768ba62Swikidesign
3485ef1705fSiLoveiDo				if ((isset($_REQUEST['comment'])) && ($_REQUEST['comment']=='edit'))
3495ef1705fSiLoveiDo					$actionDiscussion->_show(NULL, $_REQUEST['cid']);
3505ef1705fSiLoveiDo				else
3515ef1705fSiLoveiDo					$actionDiscussion->_show((($oID==$ID)?@$_REQUEST['cid']:null));
3525ef1705fSiLoveiDo
3535ef1705fSiLoveiDo			}
3545ef1705fSiLoveiDo		}
3555ef1705fSiLoveiDo		$ID = $oID;
3565ef1705fSiLoveiDo		$ADMDISCUSSION['breakaction']=true;
357a768ba62Swikidesign    }
358a768ba62Swikidesign
359a768ba62Swikidesign	/**
360a768ba62Swikidesign	 * Recursive version of glob
361a768ba62Swikidesign	 *
362a768ba62Swikidesign	 * @return array containing all pattern-matched files.
363a768ba62Swikidesign	 *
364a768ba62Swikidesign	 * @param string $sDir      Directory to start with.
365a768ba62Swikidesign	 * @param string $sPattern  Pattern to glob for.
366a768ba62Swikidesign	 * @param int $nFlags      Flags sent to glob.
367a768ba62Swikidesign	 */
368*573e23a1Swikidesign	function _globr($sDir, $sPattern, $nFlags = NULL) {
369a768ba62Swikidesign	  $sDir = escapeshellcmd($sDir);
370a768ba62Swikidesign	  // Get the list of all matching files currently in the
371a768ba62Swikidesign	  // directory.
372a768ba62Swikidesign	  $aFiles = glob("$sDir/$sPattern", $nFlags);
373a768ba62Swikidesign	  // Then get a list of all directories in this directory, and
374a768ba62Swikidesign	  // run ourselves on the resulting array.  This is the
375a768ba62Swikidesign	  // recursion step, which will not execute if there are no
376a768ba62Swikidesign	  // directories.
377a768ba62Swikidesign	  foreach (glob("$sDir/*", GLOB_ONLYDIR) as $sSubDir)  {
378a768ba62Swikidesign	   $aSubFiles = $this->globr($sSubDir, $sPattern, $nFlags);
379a768ba62Swikidesign	   $aFiles = array_merge($aFiles, $aSubFiles);
380a768ba62Swikidesign	  }
381a768ba62Swikidesign	  // The array we return contains the files we found, and the
382a768ba62Swikidesign	  // files all of our children found.
383a768ba62Swikidesign	  return $aFiles;
384a768ba62Swikidesign	}
385a768ba62Swikidesign
3865ef1705fSiLoveiDo}
387264b7327Swikidesign
388264b7327Swikidesign//Setup VIM: ex: et ts=4 enc=utf-8 :