]> git.mjollnir.org Git - moodle.git/commitdiff
"MDL-19118, comments api"
authordongsheng <dongsheng>
Fri, 24 Jul 2009 02:44:44 +0000 (02:44 +0000)
committerdongsheng <dongsheng>
Fri, 24 Jul 2009 02:44:44 +0000 (02:44 +0000)
22 files changed:
admin/settings/plugins.php
admin/settings/subsystems.php
blocks/moodleblock.class.php
comment/admin.js [new file with mode: 0644]
comment/comment.js [new file with mode: 0644]
comment/comment_ajax.php [new file with mode: 0644]
comment/comment_post.php [new file with mode: 0644]
comment/index.php [new file with mode: 0644]
comment/lib.php [new file with mode: 0644]
lang/en_utf8/admin.php
lang/en_utf8/error.php
lang/en_utf8/moodle.php
lang/en_utf8/role.php
lib/commentlib.php [new file with mode: 0644]
lib/db/access.php
lib/db/install.xml
lib/db/upgrade.php
lib/javascript-static.js
lib/moodlelib.php
lib/weblib.php
theme/standard/styles_layout.css
version.php

index e329660ef1540b88f3a9859bb45418e0a6447677..cf44967733ced81e3e18359fa6f868c12eeae71e 100644 (file)
@@ -255,6 +255,7 @@ if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext))
 }
 
 
+$ADMIN->add('reports', new admin_externalpage('comments', get_string('comments'), $CFG->wwwroot.'/comment/', 'moodle/site:viewreports'));
 /// Now add reports
 
 foreach (get_plugin_list('report') as $plugin => $plugindir) {
@@ -283,4 +284,4 @@ foreach (get_plugin_list('local') as $plugin => $plugindir) {
         include($settings_path);
         continue;
     }
-}
\ No newline at end of file
+}
index 2c9deccd055a9bce8d60d24213100543aba89a22..33b2fd2ab6cb051971d16921b3d61f319900e722 100644 (file)
@@ -5,6 +5,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $optionalsubsystems->add(new admin_setting_configcheckbox('enablegroupings', get_string('enablegroupings', 'admin'), get_string('configenablegroupings', 'admin'), 0));
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('enableoutcomes', get_string('enableoutcomes', 'grades'), get_string('configenableoutcomes', 'grades'), 0));
+    $optionalsubsystems->add(new admin_setting_configcheckbox('usecomments', get_string('enablecomments', 'admin'), get_string('configenablecomments', 'admin'), 1));
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('usetags', get_string('usetags','admin'),get_string('configusetags', 'admin'), '1'));
 
index b7609bf46dcf2875582e7c0e3cf66c497718ad3b..6adf0a7ff9a520803a197b2c652e31ee0965e630 100644 (file)
@@ -710,6 +710,29 @@ class block_base {
     function config_print() {
         throw new coding_exception('config_print() can no longer be used. Blocks should use a settings.php file.');
     }
+    /** @callback callback functions for comments api */
+    public static function comment_template($options) {
+        $ret = <<<EOD
+<div class="comment-userpicture">___picture___</div> 
+<div class="comment-content">
+    ___name___ - <span>___time___</span>
+    <div>___content___</div>
+</div>
+EOD;
+        return $ret;
+    }
+    public static function comment_permissions($options) {
+        return array('view'=>true, 'post'=>true);
+    }
+    public static function comment_url($options) {
+        return null;
+    }
+    public static function comment_display(&$comments, $options) {
+        return true;
+    }
+    public static function comment_add(&$comments, $options) {
+        return true;
+    }
 }
 
 /**
diff --git a/comment/admin.js b/comment/admin.js
new file mode 100644 (file)
index 0000000..6e25f59
--- /dev/null
@@ -0,0 +1,44 @@
+YAHOO.util.Event.onDOMReady(init); 
+function init() {
+    var select_all = document.getElementById('comment_select_all');
+    select_all.onclick = function() {
+        var comments = document.getElementsByName('comments');
+        var checked = false;
+        for (var i in comments) {
+            if (comments[i].checked) {
+                checked=true;
+            }
+        }
+        for (var i in comments) {
+            comments[i].checked = !checked;
+        }
+        this.checked = !checked;
+    }
+    var comments_delete = document.getElementById('comments_delete');
+    comments_delete.onclick = function() {
+        delete_comments();
+    }
+}
+function delete_comments() {
+    var url = moodle_cfg.wwwroot + '/comment/index.php';
+    var cb = {
+        success:function(o) {
+            if (o.responseText == 'yes') {
+                location.reload();
+            }
+        }
+    }
+    var comments = document.getElementsByName('comments');
+    var list = '';
+    for (var i in comments) {
+        if (comments[i].checked) {
+            list += (comments[i].value + '-');
+        }
+    }
+    var data = {
+        'commentids': list,
+        'sesskey': moodle_cfg.sesskey 
+    }
+    var trans = YAHOO.util.Connect.asyncRequest('POST',
+        url+'?action=delete', cb, build_querystring(data));
+}
diff --git a/comment/comment.js b/comment/comment.js
new file mode 100644 (file)
index 0000000..f3e2a96
--- /dev/null
@@ -0,0 +1,206 @@
+/**
+ * Javascript for comments 2.0
+ */
+
+function cmt_replace(client_id,list,newcmt) {
+    var ret = {};
+    ret.ids = [];
+    var template = document.getElementById('cmt-tmpl');
+    var html = '';
+    for(var i in list) {
+        var htmlid = 'comment-'+list[i].id+'-'+client_id;
+        var val = template.innerHTML;
+        val = val.replace('___name___', list[i].username);
+        if (list[i]['delete']||newcmt) {
+            list[i].time += ' <a href="###" title="'+mstr.moodle.delete+'" onclick="delete_comment(\''+client_id+'\',\''+list[i].id+'\')"><img src="'+moodle_cfg.wwwroot+'/pix/t/delete.gif" /></a>';
+        }
+        val = val.replace('___time___', list[i].time);
+        val = val.replace('___picture___', list[i].avatar);
+        val = val.replace('___content___', list[i].content);
+        val = '<li id="'+htmlid+'">'+val+'</li>';
+        ret.ids.push(htmlid);
+        html = (val+html);
+    }
+    ret.html = html;
+    return ret;
+}
+function cmt_load(cid) {
+    var container = document.getElementById('comment-list-'+cid);
+    container.innerHTML = '<div style="text-align:center"><img src="'+moodle_cfg.wwwroot+'/pix/i/loading.gif'+'" /></div>';
+}
+function get_comments(client_id, area, itemid, page) {
+    var url = moodle_cfg.wwwroot + '/comment/comment_ajax.php';
+    var data = {
+        'courseid': comment_params.courseid,
+        'contextid': comment_params.contextid,
+        'area': area,
+        'itemid': itemid,
+        'page': page,
+        'client_id': client_id,
+        'sesskey': moodle_cfg.sesskey
+    }
+    this.cb = {
+        success: function(o) {
+            try {
+                var ret = YAHOO.lang.JSON.parse(o.responseText);
+            } catch(e) {
+                alert("JSON ERROR: "+o.responseText);
+            }
+            if (!comment_check_response(ret)) {
+                return;
+            }
+            var container = document.getElementById('comment-list-'+ret.client_id);
+            var pagination = document.getElementById('comment-pagination-'+ret.client_id);
+            if (ret.pagination) {
+                pagination.innerHTML = ret.pagination;
+            } else {
+                //empty paging bar
+                pagination.innerHTML = '';
+            }
+            var result = cmt_replace(ret.client_id, ret.list);
+            container.innerHTML = result.html;
+        }
+    }
+    cmt_load(client_id);
+    var trans = YAHOO.util.Connect.asyncRequest('POST',
+        url+'?action=get', this.cb, build_querystring(data));
+}
+function post_comment(cid) {
+    this.cb = {
+        success: function(o) {
+            try {
+                var resp = YAHOO.lang.JSON.parse(o.responseText);
+            } catch(e) {
+                alert("JSON ERROR: "+o.responseText);
+            }
+            if (!comment_check_response(resp)) {
+                return;
+            }
+            if(resp) {
+                var cid = resp.client_id;
+                var ta = document.getElementById('dlg-content-'+cid);
+                ta.value = '';
+                var container = document.getElementById('comment-list-'+cid);
+                var result = cmt_replace(cid,[resp], true);
+                container.innerHTML += result.html;
+                var ids = result.ids;
+                for(var i in ids) {
+                    var attributes = {
+                        color: { to: '#06e' },
+                        backgroundColor: { to: '#FFE390' }
+                    };
+                    var anim = new YAHOO.util.ColorAnim(ids[i], attributes);
+                    anim.animate(); 
+                }
+            }
+        }
+    }
+    var ta = document.getElementById('dlg-content-'+cid);
+    if (ta.value && ta.value != mstr.moodle.addcomment) {
+        var url = moodle_cfg.wwwroot + '/comment/comment_ajax.php';
+        var formObject = document.getElementById('comment-form-'+cid);
+        YAHOO.util.Connect.setForm(formObject);
+        var trans = YAHOO.util.Connect.asyncRequest('POST', url+'?action=add', this.cb);
+    } else {
+        var attributes = {
+            backgroundColor: { from: '#FFE390', to:'#FFFFFF' }
+        };
+        var anim = new YAHOO.util.ColorAnim('dlg-content-'+cid, attributes);
+        anim.animate(); 
+    }
+}
+function delete_comment(client_id, comment_id) {
+    var url = moodle_cfg.wwwroot + '/comment/comment_ajax.php';
+    var data = {
+        'courseid': comment_params.courseid,
+        'contextid': comment_params.contextid,
+        'commentid': comment_id,
+        'client_id': client_id,
+        'sesskey': moodle_cfg.sesskey
+    }
+    this.cb = {
+        success: function(o) {
+            try {
+                var resp = YAHOO.lang.JSON.parse(o.responseText);
+            } catch(e) {
+                alert("JSON ERROR: "+o.responseText);
+            }
+            if (!comment_check_response(resp)) {
+                return;
+            }
+            var htmlid= 'comment-'+resp.commentid+'-'+resp.client_id;
+            this.el = document.getElementById(htmlid);
+            this.el.style.overflow = 'hidden';
+            var attributes = {
+                width:{to:0},
+                height:{to:0}
+            };
+            var anim = new YAHOO.util.Anim(htmlid, attributes, 1, YAHOO.util.Easing.easeOut);
+            anim.onComplete.subscribe(this.remove_dom, [], this); 
+            anim.animate(); 
+        },
+        remove_dom: function() {
+            this.el.parentNode.removeChild(this.el); 
+        }
+    }
+    var trans = YAHOO.util.Connect.asyncRequest('POST',
+        url+'?action=delete', this.cb, build_querystring(data));
+}
+function view_comments(client_id, area, itemid, page) {
+    var container = document.getElementById('comment-ctrl-'+client_id);
+    var ta = document.getElementById('dlg-content-'+client_id);
+    var img = document.getElementById('comment-img-'+client_id);
+    if (container.style.display=='none'||container.style.display=='') {
+        // show
+        get_comments(client_id, area, itemid, page);
+        container.style.display = 'block';
+        img.src=moodle_cfg.wwwroot+'/pix/t/expanded.png';
+    } else {
+        // hide
+        container.style.display = 'none';
+        img.src=moodle_cfg.wwwroot+'/pix/t/collapsed.png';
+        ta.value = '';
+    }
+    toggle_textarea.apply(ta, [false]);
+    // reset textarea size
+    ta.onclick = function() {
+        toggle_textarea.apply(this, [true]);
+    }
+    ta.onkeypress = function() {
+        if (this.scrollHeight > this.clientHeight && !window.opera)
+            this.rows += 1;
+    }
+    ta.onblur = function() {
+        toggle_textarea.apply(this, [false]);
+    }
+    return false;
+}
+function comment_hide_link(cid) {
+    var link = document.getElementById('comment-link-'+cid);
+    if(link){
+        link.style.display='none';
+    } else {
+        alert('wront');
+    }
+}
+function toggle_textarea(focus) {
+    if (focus) {
+        if (this.value == mstr.moodle.addcomment) {
+            this.value = '';
+            this.style.color = 'black';
+        }
+    }else{
+        if (this.value == '') {
+            this.value = mstr.moodle.addcomment;
+            this.style.color = 'grey';
+            this.rows = 1;
+        }
+    }
+}
+function comment_check_response(data) {
+    if (data.error) {
+        alert(data.error);
+        return false;
+    }
+    return true;
+}
diff --git a/comment/comment_ajax.php b/comment/comment_ajax.php
new file mode 100644 (file)
index 0000000..f1cd0f2
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/*
+ * Handling all ajax request for comments API
+ */
+require_once('../config.php');
+require_once($CFG->libdir.'/commentlib.php');
+
+$courseid  = optional_param('courseid',  SITEID, PARAM_INT);
+$contextid = optional_param('contextid', SYSCONTEXTID, PARAM_INT);
+
+$context   = get_context_instance_by_id($contextid);
+$cm        = get_coursemodule_from_id('', $context->instanceid);
+require_login($courseid, true, $cm);
+
+$err = new stdclass;
+
+if (!confirm_sesskey()) {
+    $err->error = get_string('invalidsesskey');
+    die(json_encode($err));
+}
+
+if (!isloggedin()){
+    $err->error = get_string('loggedinnot');
+    die(json_encode($err));
+}
+
+if (isguestuser()) {
+    $err->error = get_string('loggedinnot');
+    die(json_encode($err));
+}
+
+$action    = optional_param('action',    '',     PARAM_ALPHA);
+$area      = optional_param('area',      '',     PARAM_ALPHAEXT);
+$client_id = optional_param('client_id', '',     PARAM_RAW);
+$commentid = optional_param('commentid', -1,     PARAM_INT);
+$content   = optional_param('content',   '',     PARAM_RAW);
+$itemid    = optional_param('itemid',    '',     PARAM_INT);
+$page      = optional_param('page',      0,      PARAM_INT);
+
+if (!empty($client_id)) {
+    $cmt = new stdclass;
+    $cmt->contextid = $contextid;
+    $cmt->courseid  = $courseid;
+    $cmt->area      = $area;
+    $cmt->itemid    = $itemid;
+    $cmt->client_id = $client_id;
+    $comment = new comment($cmt);
+}
+switch ($action) {
+case 'add':
+    $cmt = $comment->add($content);
+    if (!empty($cmt) && is_object($cmt)) {
+        $cmt->client_id = $client_id;
+        echo json_encode($cmt);
+    } else if ($cmt === COMMENT_ERROR_DB) {
+        $err->error = get_string('dbupdatefailed');
+        echo json_encode($err);
+    } else if ($cmt === COMMENT_ERROR_MODULE_REJECT) {
+        $err->error = get_string('modulererejectcomment');
+        echo json_encode($err);
+    } else if ($cmt === COMMENT_ERROR_INSUFFICIENT_CAPS) {
+        $err->error = get_string('nopermissiontocomment');
+        echo json_encode($err);
+    }
+    break;
+case 'delete':
+    $result = $comment->delete($commentid);
+    if ($result === true) {
+        echo json_encode(array('client_id'=>$client_id, 'commentid'=>$commentid));
+    } else if ($result == COMMENT_ERROR_INSUFFICIENT_CAPS) {
+        $err->error = get_string('nopermissiontoeditcomment');
+        echo json_encode($err);
+    } else if ($result == COMMENT_ERROR_DB) {
+        $err->error = get_string('dbupdatefailed');
+        echo json_encode($err);
+    }
+    break;
+case 'get':
+default:
+    $ret = array();
+    $comments = $comment->get_comments($page);
+    $ret['list'] = $comments;
+    $ret['pagination'] = $comment->get_pagination($page);
+    $ret['client_id']  = $client_id;
+    echo json_encode($ret);
+    exit;
+}
diff --git a/comment/comment_post.php b/comment/comment_post.php
new file mode 100644 (file)
index 0000000..ca557f3
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/*
+ * Handling all ajax request for comments API
+ */
+require_once('../config.php');
+require_once($CFG->libdir.'/commentlib.php');
+
+$courseid  = optional_param('courseid',  SITEID, PARAM_INT);
+$contextid = optional_param('contextid', SYSCONTEXTID, PARAM_INT);
+
+$context   = get_context_instance_by_id($contextid);
+$cm        = get_coursemodule_from_id('', $context->instanceid);
+require_login($courseid, true, $cm);
+
+$err = new stdclass;
+
+if (!confirm_sesskey()) {
+    print_error('invalidsesskey');
+}
+
+if (!isloggedin()){
+    print_error('loggedinnot');
+}
+
+if (isguestuser()) {
+    print_error('loggedinnot');
+}
+
+$action    = optional_param('action',    '',     PARAM_ALPHA);
+$area      = optional_param('area',      '',     PARAM_ALPHAEXT);
+$commentid = optional_param('commentid', -1,     PARAM_INT);
+$content   = optional_param('content',   '',     PARAM_RAW);
+$itemid    = optional_param('itemid',    '',     PARAM_INT);
+$returnurl = optional_param('returnurl', '',     PARAM_URL);
+
+$cmt = new stdclass;
+$cmt->contextid = $contextid;
+$cmt->courseid  = $courseid;
+$cmt->area      = $area;
+$cmt->itemid    = $itemid;
+$comment = new comment($cmt);
+
+switch ($action) {
+case 'add':
+    $cmt = $comment->add($content);
+    if (!empty($cmt) && is_object($cmt)) {
+        redirect($returnurl, get_string('pageshouldredirect'));
+    } else if ($cmt === COMMENT_ERROR_DB) {
+        print_error('dbupdatefailed');
+    } else if ($cmt === COMMENT_ERROR_MODULE_REJECT) {
+        print_error('modulererejectcomment');
+    } else if ($cmt === COMMENT_ERROR_INSUFFICIENT_CAPS) {
+        print_error('nopermissiontocomment');
+    }
+    break;
+default:
+    exit;
+}
diff --git a/comment/index.php b/comment/index.php
new file mode 100644 (file)
index 0000000..ebbe3f3
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/*
+ * Comments management interface
+ */
+require_once('../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once('lib.php');
+$context = get_context_instance(CONTEXT_SYSTEM);
+require_capability('moodle/comment:delete', $context);
+$PAGE->requires->yui_lib('yahoo')->in_head();
+$PAGE->requires->yui_lib('dom')->in_head();
+$PAGE->requires->yui_lib('event')->in_head();
+$PAGE->requires->yui_lib('animation')->in_head();
+$PAGE->requires->yui_lib('json')->in_head();
+$PAGE->requires->yui_lib('connection')->in_head();
+$PAGE->requires->js('comment/admin.js')->in_head();
+
+$action     = optional_param('action', '', PARAM_ALPHA);
+$commentid  = optional_param('commentid', 0, PARAM_INT);
+$commentids = optional_param('commentids', '', PARAM_ALPHANUMEXT);
+$page       = optional_param('page', 0, PARAM_INT);
+$manager = new comment_manager();
+
+if (!empty($action)) {
+    confirm_sesskey();
+}
+
+if ($action === 'delete') {
+    // delete a single comment
+    if (!empty($commentid)) {
+        if ($manager->delete_comment($commentid)) {
+            redirect($CFG->httpswwwroot.'/comment/', get_string('deleted'));
+        } else {
+            $err = 'cannotdeletecomment';
+        }
+    }
+    // delete a list of comments
+    if (!empty($commentids)) {
+        if ($manager->delete_comments($commentids)) {
+            die('yes');
+        } else {
+            die('no');
+        }
+    }
+}
+
+admin_externalpage_setup('comments');
+admin_externalpage_print_header();
+print_heading(get_string('comments'));
+if (!empty($err)) {
+    print_error($err, 'error', $CFG->httpswwwroot.'/comment/');
+}
+if (empty($action)) {
+    $manager->print_comments($page);
+    echo '<div class="mdl-align">';
+    echo '<button id="comments_delete">'.get_string('delete').'</button>';
+    echo '<div>';
+}
+admin_externalpage_print_footer();
diff --git a/comment/lib.php b/comment/lib.php
new file mode 100644 (file)
index 0000000..b7ab939
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * comment_manager is class to manage moodle comments
+ *
+ * @package   moodlecore
+ * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class comment_manager {
+
+    /**
+     * Return comments by pages
+     * @param int $page 
+     * @return mixed
+     */
+    function get_comments($page) {
+        global $DB, $CFG, $USER;
+        $params = array();
+        if ($page == 0) {
+            $start = 0;
+        } else {
+            $start = $page*$this->perpage;
+        }
+        $sql = "SELECT c.id, c.contextid, c.itemid, c.commentarea, c.userid, c.content, u.username, u.firstname, u.lastname, c.timecreated 
+            FROM {comments} c, {user} u
+            WHERE u.id=c.userid ORDER BY c.timecreated ASC";
+
+        $comments = array();
+        if ($records = $DB->get_records_sql($sql, array(), $start, $this->perpage)) {
+            foreach ($records as $item) {
+                $item->username = fullname($item);
+                $item->time = userdate($item->timecreated);
+                $item->content = format_text($item->content);
+                $comments[] = $item;
+                unset($item->firstname);
+                unset($item->lastname);
+                unset($item->timecreated);
+            }
+        }
+
+        return $comments;
+    }
+
+    private function _setup_course($courseid) {
+        global $COURSE, $DB;
+        if (!empty($this->course)) {
+            // already set, stop
+            return;
+        }
+        if ($courseid == $COURSE->id) {
+            $this->course = $COURSE;
+        } else if (!$this->course = $DB->get_record('course', array('id'=>$courseid))) {
+            $this->course = null;
+        }
+    }
+
+    private function _setup_plugin($comment) {
+        global $DB;
+        $this->context = get_context_instance_by_id($comment->contextid);
+        if ($this->context->contextlevel == CONTEXT_BLOCK) {
+            if ($block = $DB->get_record('block_instances', array('id'=>$this->context->instanceid))) {
+                $this->plugintype = 'block';
+                $this->pluginname = $block->blockname;
+            }
+        }
+        if ($this->context->contextlevel == CONTEXT_MODULE) {
+            $this->plugintype = 'mod';
+            $this->cm = get_coursemodule_from_id('', $this->context->instanceid);
+            $this->_setup_course($this->cm->course);
+            $this->modinfo = get_fast_modinfo($this->course);
+            $this->pluginname = $this->modinfo->cms[$this->cm->id]->modname;
+        }
+    }
+
+    /**
+     * Print comments
+     * @param int $page 
+     */
+    function print_comments($page=0) {
+        global $CFG, $OUTPUT, $DB;
+        $this->perpage = 10;
+        $count = $DB->count_records_sql('SELECT COUNT(*) FROM {comments} c');
+        $comments = $this->get_comments($page);
+        $table = new stdclass;
+        $table->head = array ('<input type="checkbox" id="comment_select_all"/>', 'author', 'content', 'action');
+        $table->align = array ('left', 'left', 'left', 'left');
+        $table->width = "95%";
+        $table->data = array();
+        foreach ($comments as $c) {
+            $this->_setup_plugin($c);
+            if (!empty($this->plugintype)) {
+                $url = plugin_callback($this->plugintype, $this->pluginname, FEATURE_COMMENT, 'url', array($c));
+            }
+            $checkbox = '<input type="checkbox" name="comments" value="'. $c->id .'" />';
+            $action = '';
+            $action .= "<a href='{$CFG->wwwroot}/comment/index.php?action=delete&amp;sesskey=".sesskey()."&amp;commentid={$c->id}'>".get_string('delete').'</a>';
+            $action .= "<br />";
+            if (!empty($url)) {
+                $action .= "<a target='_blank' href='{$url}'>".get_string('commentincontext').'</a>';
+            }
+            $table->data[] = array($checkbox, $c->username, $c->content, $action);
+        } 
+        print_table($table);
+        print_paging_bar($count, $page, $this->perpage, $CFG->wwwroot.'/comment/index.php?', 'page');
+    }
+
+    /**
+     * delete a comment
+     * @param int $commentid 
+     */
+    public function delete_comment($commentid) {
+        global $DB;
+        if ($comment = $DB->get_record('comments', array('id'=>$commentid))) {
+            return $DB->delete_records('comments', array('id'=>$commentid));
+        }
+        return false;
+    }
+    /**
+     * delete comments
+     * @param int $commentid 
+     */
+    public function delete_comments($list) {
+        global $DB;
+        $ids = explode('-', $list);
+        foreach ($ids as $id) {
+            if (is_int((int)$id)) {
+                if ($comment = $DB->get_record('comments', array('id'=>$id))) {
+                    $DB->delete_records('comments', array('id'=>$comment->id));
+                }
+            }
+        }
+        return true;
+    }
+}
index 8677e2d2596dcfc0308cb6075d16c57863b9724c..c403e9fd9d3063748f304b234b0ea592cdac65ad 100644 (file)
@@ -136,6 +136,7 @@ $string['configenableajax'] = 'This setting allows you to control the use of AJA
 $string['configdisablecourseajax'] = 'Do not use AJAX when editing main course pages.';
 $string['configenablecalendarexport'] = 'Enable exporting or subscribing to calendars.';
 $string['configenablecourserequests'] = 'This will allow any user to request a course be created.';
+$string['configenablecomments'] = 'Enable comments';
 $string['configenableglobalsearch'] = 'This setting enables global text searching in resources and activities, it is not compatible with PHP 4.';
 $string['configenablegroupings'] = 'This setting enables groupings of groups.';
 $string['configenablehtmlpurifier'] = 'Use HTML Purifier instead of KSES for cleaning of untrusted text. HTML Purifier is actively developed and is believed to be more secure, but it is more resource intensive. Expect minor visual differences in the resulting html code. Please note that embed and object tags can not be enabled, MathML tags and old lang tags are not supported. ';
@@ -393,6 +394,7 @@ $string['emptysettingvalue'] = 'Empty';
 $string['enableajax'] = 'Enable AJAX';
 $string['enablecalendarexport'] = 'Enable calendar export';
 $string['enablecourserequests'] = 'Enable course requests';
+$string['enablecomments'] = 'Enable comments';
 $string['enableglobalsearch'] = 'Enable global search';
 $string['enablegroupings'] = 'Enable groupings';
 $string['enablehtmlpurifier'] = 'Enable HTML Purifier';
index ac98bf96a182cd407ffde637802c94c68ecf0e94..f025424d72cb132cb0ffdd6b42634206afb430b2 100644 (file)
@@ -319,6 +319,7 @@ $string['moduledoesnotexist'] = 'This module does not exist';
 $string['moduleinstancedoesnotexist'] = 'The instance of this module does not exist';
 $string['moduledisable'] = 'This module ($a) has been disabled for this particular course';
 $string['modulemissingcode'] = 'Module $a is missing the code needed to perform this function';
+$string['modulerejectcomment'] = 'Module rejects to add this comment';
 $string['mustbeteacher'] = 'You must be a teacher to look at this page';
 $string['multiplerecordsfound'] = 'Multiple records found, only one record expected.';
 $string['multiplerestorenotallow'] = 'Multiple restore execution not allowed!';
@@ -344,7 +345,7 @@ $string['nonmeaningfulcontent'] = 'Non meaningful content';
 $string['noparticipatorycms'] = 'Sorry, but you have no participatory course modules to report on';
 $string['noparticipants'] = 'No participants found for this course';
 $string['nopermissions'] = 'Sorry, but you do not currently have permissions to do that ($a)';
-$string['nopermissiontocomment'] = 'You can\'t add comments to this glossary!';
+$string['nopermissiontocomment'] = 'You can\'t add comments';
 $string['nopermissiontodelentry'] = 'You can\'t delete other people\'s entries!';
 $string['nopermissiontoeditcomment'] = 'You can\'t edit other people\'s comments!';
 $string['nopermissiontohide'] = 'No permission to hide!';
index 319b2e7623920e4d7ad1c6cfc856fea65884a814..f4547f56bc358ab359565d2e389ca04475e2fe8f 100644 (file)
@@ -23,6 +23,7 @@ $string['addactivity'] = 'Add an activity...';
 $string['addadmin'] = 'Add admin';
 $string['addblock'] = 'Add a block';
 $string['addcreator'] = 'Add course creator';
+$string['addcomment'] = 'Add a comment...';
 $string['adddots'] = 'Add...';
 $string['added'] = 'Added $a';
 $string['addedrecip'] = 'Added $a new recipient';
@@ -245,6 +246,8 @@ $string['clickhere'] = 'Click here ...';
 $string['clicktochange'] = 'Click to change';
 $string['clicktohideshow'] = 'Click to expand or collapse';
 $string['closewindow'] = 'Close this window';
+$string['comments'] = 'Comments';
+$string['commentincontext'] = 'Find this comment in context';
 $string['comparelanguage'] = 'Compare and edit current language';
 $string['complete'] = 'Complete';
 $string['completereport'] = 'Complete report';
@@ -1430,6 +1433,7 @@ $string['showalltopics'] = 'Show all topics';
 $string['showallusers'] = 'Show all users';
 $string['showallweeks'] = 'Show all weeks';
 $string['showblockcourse'] = 'Show list of courses containing block';
+$string['showcomments'] = 'Show/hide comments';
 $string['showgrades'] = 'Show gradebook to students';
 $string['showlistofcourses'] = 'Show list of courses';
 $string['showmodulecourse'] = 'Show list of courses containing activity';
index 83ec50a4c6dc22afcb7764ed000164127966cc0a..8dd53a50454adea146fbee9048145fd25bc06610 100644 (file)
@@ -47,6 +47,9 @@ $string['checkpermissionsin'] = 'Check permissions in $a';
 $string['checksystempermissionsfor'] = 'Check system permissions for $a->fullname';
 $string['checkuserspermissionshere'] = 'Check permissions for $a->fullname has in this $a->contextlevel';
 $string['chooseroletoassign'] = 'Please choose a role to assign';
+$string['comment:delete'] = 'Delete comments';
+$string['comment:post'] = 'Post comments';
+$string['comment:view'] = 'Read comments';
 $string['context'] = 'Context';
 $string['course:activityvisibility'] = 'Hide/show activities';
 $string['course:bulkmessaging'] = 'Send a message to many people';
diff --git a/lib/commentlib.php b/lib/commentlib.php
new file mode 100644 (file)
index 0000000..53ca35b
--- /dev/null
@@ -0,0 +1,558 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+define('COMMENT_ERROR_DB', 1);
+define('COMMENT_ERROR_INSUFFICIENT_CAPS', 2);
+define('COMMENT_ERROR_MODULE_REJECT', 3);
+require_once($CFG->dirroot.'/lib/formslib.php');
+
+class comment_form extends moodleform {
+
+    // Define the form
+    function definition () {
+        global $USER, $CFG, $COURSE;
+
+        $mform =& $this->_form;
+
+        $mform->addElement('textarea', 'content', get_string('addcomment'));
+        $mform->addElement('hidden', 'contextid');
+        $mform->addElement('hidden', 'itemid');
+        $mform->addElement('hidden', 'area');
+        $mform->addElement('hidden', 'courseid');
+        $mform->addElement('hidden', 'returnurl');
+
+        $this->add_action_buttons();
+    }
+}
+/**
+ * comment is class to process moodle comments
+ *
+ * @package   moodlecore
+ * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class comment {
+    /**
+     * @var integer
+     */
+    private $page;
+    /**
+     * there may be several comment box in one page
+     * so we need a client_id to recognize them
+     * @var integer
+     */
+    private $cid;
+    private $contextid;
+    /**
+     * commentarea is used to specify different
+     * parts shared the same itemid
+     * @var string
+     */
+    private $commentarea;
+    /**
+     * itemid is used to associate with commenting content 
+     * @var integer
+     */
+    private $itemid;
+
+    /**
+     * this html snippet will be used as a template
+     * to build comment content
+     * @var string
+     */
+    private $template;
+    private $context;
+    private $course;
+    /**
+     * course module object
+     * @var string
+     */
+    private $cm;
+    private $plugintype;
+    private $pluginname;
+    private $viewcap;
+    private $postcap;
+    /**
+     * to tell comments api where it is used
+     * @var string
+     */
+    private $env;
+    /**
+     * to costomize link text
+     * @var string
+     */
+    private $linktext;
+    private static $nonjs = false;
+    private static $commentform = null;
+    // will be used by non-js comments UI
+    private static $comment_itemid = null;
+    private static $comment_context = null;
+    private static $comment_area = null; 
+    /**
+     * Construct function of comment class, initilize 
+     * class members
+     * @param object $options
+     */
+    public function __construct($options) {
+        global $CFG;
+        $this->viewcap = false;
+        $this->postcap = false;
+        if (!empty($options->client_id)) {
+            $this->cid = $options->client_id;
+        } else {
+            $this->cid = uniqid();
+        }
+        if (!empty($options->context)) {
+            $this->context = $options->context;
+            $this->contextid = $this->context->id;
+        } else if(!empty($options->contextid)) {
+            $this->contextid = $options->contextid;
+            $this->context = get_context_instance_by_id($this->contextid);
+        } else {
+            print_error('invalidcontext');
+        }
+        if (!empty($options->area)) {
+            $this->commentarea = $options->area;
+        }
+        if (!empty($options->itemid)) {
+            $this->itemid = $options->itemid;
+        }
+        if (!empty($options->env)) {
+            $this->env = $options->env;
+        } else {
+            $this->env = '';
+        }
+
+        if (!empty($options->linktext)) {
+            $this->linktext = $options->linktext;
+        } else {
+            $this->linktext = get_string('comments');
+        }
+
+        $this->_setup_plugin();
+
+        $this->options = new stdclass;
+        $this->options->context     = $this->context;
+        $this->options->commentarea = $this->commentarea;
+        $this->options->itemid      = $this->itemid;
+
+        // load template
+        $this->template = <<<EOD
+<div class="comment-userpicture">___picture___</div> 
+<div class="comment-content">
+    ___name___ - <span>___time___</span>
+    <div>___content___</div>
+</div>
+EOD;
+        if (!empty($this->plugintype)) {
+            $this->template = plugin_callback($this->plugintype, $this->pluginname, FEATURE_COMMENT, 'template', $this->options, $this->template);
+        }
+
+        // setting post and view permissions
+        $this->_check_permissions();
+
+        // setup course
+        if (!empty($options->course)) {
+            $this->course   = $options->course;
+        } else if (!empty($options->courseid)) {
+            $courseid = $options->courseid;
+            $this->_setup_course($courseid);
+        } else {
+            $courseid = SITEID;
+            $this->_setup_course($courseid);
+        }
+
+        unset($options);
+    }
+
+    /**
+     * Print required YUI libraries, must be called before html head printed
+     * @return boolean
+     */
+    public static function js() {
+        global $PAGE, $CFG;
+        // setup variables for non-js interface
+        self::$nonjs = optional_param('nonjscomment', '', PARAM_ALPHA);
+        self::$comment_itemid  = optional_param('comment_itemid',  '', PARAM_INT);
+        self::$comment_context = optional_param('comment_context', '', PARAM_INT);
+        self::$comment_area    = optional_param('comment_area',    '', PARAM_ALPHAEXT);
+        if (!empty(self::$nonjs)) {
+            self::$commentform = new comment_form($CFG->httpswwwroot . '/comment/comment_post.php?action=add');
+        }
+
+        $PAGE->requires->yui_lib('yahoo')->in_head();
+        $PAGE->requires->yui_lib('dom')->in_head();
+        $PAGE->requires->yui_lib('event')->in_head();
+        $PAGE->requires->yui_lib('animation')->in_head();
+        $PAGE->requires->yui_lib('json')->in_head();
+        $PAGE->requires->yui_lib('connection')->in_head();
+        $PAGE->requires->js('comment/comment.js')->in_head();
+        $PAGE->requires->string_for_js('addcomment', 'moodle');
+        $PAGE->requires->string_for_js('delete', 'moodle');
+    }
+
+    private function _setup_course($courseid) {
+        global $COURSE, $DB;
+        if (!empty($this->course)) {
+            // already set, stop
+            return;
+        }
+        if ($courseid == $COURSE->id) {
+            $this->course = $COURSE;
+        } else if (!$this->course = $DB->get_record('course', array('id'=>$courseid))) {
+            $this->course = null;
+        }
+    }
+
+    /**
+     * Setting module info
+     *
+     */
+    private function _setup_plugin() {
+        global $DB;
+        // blog needs to set env as "blog"
+        if ($this->env == 'blog') {
+            $this->plugintype = 'moodle';
+            $this->pluginname = 'blog';
+        }
+        // tag page needs to set env as "tag"
+        if ($this->env == 'tag') {
+            $this->plugintype = 'moodle';
+            $this->pluginname = 'tag';
+        }
+        if ($this->context->contextlevel == CONTEXT_BLOCK) {
+            if ($block = $DB->get_record('block_instances', array('id'=>$this->context->instanceid))) {
+                $this->plugintype = 'block';
+                $this->pluginname = $block->blockname;
+            }
+        }
+        if ($this->context->contextlevel == CONTEXT_MODULE) {
+            $this->plugintype = 'mod';
+            $this->cm = get_coursemodule_from_id('', $this->context->instanceid);
+            $this->_setup_course($this->cm->course);
+            $this->modinfo = get_fast_modinfo($this->course);
+            $this->pluginname = $this->modinfo->cms[$this->cm->id]->modname;
+        }
+    }
+
+    /**
+     * check posting comments permission
+     * It will check based on user roles and ask modules
+     * If you need to check permission by modules, a 
+     * function named $pluginname_check_comment_post must be implemented
+     */
+    private function _check_permissions() {
+        global $CFG;
+        $this->postcap = has_capability('moodle/comment:post', $this->context);
+        $this->viewcap = has_capability('moodle/comment:view', $this->context);
+        if (!empty($this->plugintype)) {
+            $permissions = plugin_callback($this->plugintype, $this->pluginname, FEATURE_COMMENT, 'permissions', $this->options, array('post'=>true, 'view'=>true));
+            $this->postcap = $this->postcap && $permissions['post'];
+            $this->viewcap = $this->viewcap && $permissions['view'];
+        }
+    }
+
+    /**
+     * Prepare comment code in html
+     * @param boolean
+     * @return mixed
+     */
+    public function init($return = true) {
+        global $CFG, $COURSE, $PAGE;
+
+        $this->link = qualified_me();
+        $murl = new moodle_url($this->link);
+        $murl->remove_params('nonjscomment');
+        $murl->param('nonjscomment', 'true');
+        $murl->param('comment_itemid', $this->options->itemid);
+        $murl->param('comment_context', $this->options->context->id);
+        $murl->param('comment_area', $this->options->commentarea);
+        $murl->remove_params('page');
+        $this->link = $murl->out();
+        if ($this->env === 'block_comments') {
+            // auto expends comments
+            $PAGE->requires->js_function_call('view_comments', array($this->cid, $this->commentarea, $this->itemid, 0))->on_dom_ready();
+            $PAGE->requires->js_function_call('comment_hide_link', array($this->cid))->on_dom_ready();
+        }
+
+        if (!empty(self::$nonjs)) {
+            return $this->print_comments($this->page, $return);
+        }
+        $strsubmit = get_string('submit');
+        $strcancel = get_string('cancel');
+        $sesskey = sesskey();
+
+        // print html template
+        if (empty($CFG->commentcommentcode) && !empty($this->viewcap)) {
+            echo '<div style="display:none" id="cmt-tmpl">' . $this->template . '</div>';
+
+            // shared parameters for commenting
+            $params = new stdclass;
+            $params->courseid = $this->course->id;
+            $params->contextid = $this->contextid;
+            $PAGE->requires->data_for_js('comment_params', $params);
+
+            $CFG->commentcommentcode = true;
+        }
+
+        if (!empty($this->viewcap)) {
+            // print commenting icon and tooltip
+            $html = <<<EOD
+<a id="comment-link-{$this->cid}" onclick="return view_comments('{$this->cid}', '{$this->commentarea}', '{$this->itemid}', 0)" href="{$this->link}">
+<img id="comment-img-{$this->cid}" src="{$CFG->wwwroot}/pix/t/collapsed.png" alt="{$this->linktext}" title="{$this->linktext}" /> 
+<span>{$this->linktext}</span>
+</a>
+<div id="comment-ctrl-{$this->cid}" class="comment-ctrl">
+    <div class="comment-list-wrapper">
+            <ul id="comment-list-{$this->cid}" class="comment-list">
+            </ul>
+    </div>
+    <div id="comment-pagination-{$this->cid}" class="comment-pagination"></div>
+EOD;
+
+            // print posting textarea
+            if (!empty($this->postcap)) {
+                $html .= <<<EOD
+<div class='comment-area'>
+    <div class="bd">
+        <form method="POST" id="comment-form-{$this->cid}" action="{$CFG->wwwroot}/admin/comments.php">
+        <textarea name="content" rows="1" id="dlg-content-{$this->cid}"></textarea>
+        <input type="hidden" name="contextid" value="$this->contextid" />
+        <input type="hidden" name="action" value="add" />
+        <input type="hidden" name="area" value="$this->commentarea" />
+        <input type="hidden" name="itemid" value="$this->itemid" />
+        <input type="hidden" name="courseid" value="{$this->course->id}" />
+        <input type="hidden" name="sesskey" value="{$sesskey}" />
+        <input type="hidden" name="client_id" value="$this->cid" />
+        </form>
+    </div>
+    <div class="fd" id="comment-action-{$this->cid}">
+        <a href="###" onclick="post_comment('{$this->cid}')"> {$strsubmit} </a>
+        <span> | </span>
+        <a href="###" onclick="view_comments('{$this->cid}')"> {$strcancel} </a>
+    </div>
+</div>
+<div style="clear:both"></div>
+EOD;
+            }
+
+            $html .= <<<EOD
+</div>
+EOD;
+        } else {
+            $html = '';
+        }
+
+        if ($return) {
+            return $html;
+        } else {
+            echo $html;
+        }
+    }
+
+    /**
+     * Return matched comments 
+     * @param int $page 
+     * @return mixed
+     */
+    public function get_comments($page = '') {
+        global $DB, $CFG, $USER;
+        if (empty($this->viewcap)) {
+            return false;
+        }
+        // TODO: find a apropriate add page to add this option
+        $CFG->commentsperpage = 15;
+        if (!is_numeric($page)) {
+            $page = 0;
+        }
+        $this->page = $page;
+        $params = array();
+        $start = $page * $CFG->commentsperpage;
+        $sql = "SELECT c.id, c.userid, c.content, c.format, c.timecreated, u.picture, u.imagealt, u.username, u.firstname, u.lastname
+            FROM {comments} c, {user} u WHERE u.id=c.userid AND c.contextid=? AND c.commentarea=? AND c.itemid=?
+            ORDER BY c.timecreated DESC";
+        $params[] = $this->contextid;
+        $params[] = $this->commentarea;
+        $params[] = $this->itemid;
+
+        $comments = array();
+        $candelete = has_capability('moodle/comment:delete', $this->context);
+        if ($records = $DB->get_records_sql($sql, $params, $start, $CFG->commentsperpage)) {
+            foreach ($records as &$c) {
+                $url = $CFG->httpswwwroot.'/user/view.php?id='.$c->userid.'&amp;course='.$this->course->id;
+                $c->username = '<a href="'.$url.'">'.fullname($c).'</a>';
+                $c->time = userdate($c->timecreated);
+                $user = new stdclass;
+                $user->id = $c->userid;
+                $user->picture = $c->picture;
+                $user->firstname = $c->firstname;
+                $user->lastname  = $c->lastname;
+                $user->imagealt  = $c->imagealt;
+                $c->content = format_text($c->content, $c->format);
+                $c->avatar = print_user_picture($user, $this->course->id, NULL, NULL, true);
+                if (($USER->id == $c->userid) || !empty($candelete)) {
+                    $c->delete = true;
+                } 
+                $comments[] = $c;
+            }
+        }
+
+        if (!empty($this->plugintype)) {
+            // moodle module will filter comments
+            plugin_callback($this->plugintype, $this->pluginname, FEATURE_COMMENT, 'display', array(&$comments, $this->options));
+        }
+
+        return $comments;
+    }
+
+    public function get_pagination($page = 0) {
+        global $DB, $CFG;
+        if (!$count = $DB->count_records_sql("SELECT COUNT(*) from {comments} c, {user} u WHERE u.id=c.userid AND c.contextid=? AND c.commentarea=? AND c.itemid=?", array($this->contextid, $this->commentarea, $this->itemid))) {
+        }
+        $pages = (int)ceil($count/$CFG->commentsperpage);
+        if ($pages == 1 || $pages == 0) {
+            return '';
+        }
+        if (!empty(self::$nonjs)) {
+            // used in non-js interface
+            return print_paging_bar($count, $page, $CFG->commentsperpage, $this->link.'&amp;', $pagevar='page', false, true);
+        } else {
+            // return ajax paging bar
+            $str = '';
+            $str .= '<div class="comment-paging">';
+            for ($p=0; $p<$pages; $p++) {
+                $extra = '';
+                if ($p == $page) {
+                    $extra = ' style="border:1px solid grey" ';
+                }
+                $str .= '<a class="pageno" href="###"'.$extra.' onclick="get_comments(\''.$this->cid.'\', \''.$this->commentarea.'\', \''.$this->itemid.'\', \''.$p.'\')">'.($p+1).'</a> ';
+            }
+            $str .= '</div>';
+        }
+        return $str;
+    }
+
+    /**
+     * Add a new comment
+     * @param string $content 
+     * @return mixed
+     */
+    public function add($content) {
+        global $CFG, $DB, $USER;
+        if (empty($this->postcap)) {
+            return COMMENT_ERROR_INSUFFICIENT_CAPS;
+        }
+        $now = time();
+        $newcmt = new stdclass;
+        $newcmt->contextid    = $this->contextid;
+        $newcmt->commentarea  = $this->commentarea;
+        $newcmt->itemid       = $this->itemid;
+        $newcmt->content      = $content;
+        $newcmt->format       = FORMAT_MOODLE;
+        $newcmt->userid       = $USER->id;
+        $newcmt->timecreated  = $now;
+
+        if (!empty($this->plugintype)) {
+            // moodle module will check content
+            $ret = plugin_callback($this->plugintype, $this->pluginname, FEATURE_COMMENT, 'add', array(&$newcmt, $this->options), true);
+            if (!$ret) {
+                return COMMENT_ERROR_MODULE_REJECT;
+            }
+        }
+
+        $cmt_id = $DB->insert_record('comments', $newcmt);
+        if (!empty($cmt_id)) {
+            $newcmt->id = $cmt_id;
+            $newcmt->time = userdate($now);
+            $newcmt->username = fullname($USER);
+            $newcmt->content = format_text($newcmt->content);
+            $newcmt->avatar = print_user_picture($USER, $this->course->id, NULL, 16, true);
+            return $newcmt;
+        } else {
+            return COMMENT_ERROR_DB;
+        }
+    }
+
+    /**
+     * delete a comment
+     * @param int $commentid 
+     * @return mixed
+     */
+    public function delete($commentid) {
+        global $DB, $USER;
+        $candelete = has_capability('moodle/comment:delete', $this->context);
+        if (!$comment = $DB->get_record('comments', array('id'=>$commentid))) {
+            return COMMENT_ERROR_DB;
+        }
+        if (!($USER->id == $comment->userid || !empty($candelete))) {
+            return COMMENT_ERROR_INSUFFICIENT_CAPS;
+        }
+        return $DB->delete_records('comments', array('id'=>$commentid));
+    }
+
+    public function print_comments($page = 0, $return = true) {
+        global $DB, $CFG;
+        $html = '';
+        if (!(self::$comment_itemid == $this->options->itemid &&
+            self::$comment_context == $this->options->context->id && 
+            self::$comment_area == $this->options->commentarea)) {
+            $page = 0;
+        } 
+        $comments = $this->get_comments($page);
+
+        $html .= '<h3>'.get_string('comments').'</h3>';
+        $html .= "<ul id='comment-list-$this->cid' class='comment-list'>";
+        $results = array();
+        $list = '';
+        foreach ($comments as $cmt) {
+            $list = $this->print_comment($cmt, $this->contextid, $this->commentarea, $this->itemid) . $list;
+        }
+        $html .= $list;
+        $html .= '</ul>';
+        $html .= $this->get_pagination($page);
+        self::$commentform->set_data(array('itemid'=>$this->itemid, 'courseid'=>$this->course->id, 'contextid'=>$this->contextid, 'area'=>$this->commentarea, 'returnurl'=>qualified_me()));
+        if ($return) {
+            //ob_start();
+            //self::$commentform->display();
+            //$formhtml = ob_get_contents();
+            //return $html . $formhtml;
+            return $html;
+        } else {
+            echo $html;
+            self::$commentform->display();
+        }
+    }
+
+    public function print_comment($cmt) {
+        $patterns = array();
+        $replacements = array();
+
+        $patterns[] = '___picture___';
+        $patterns[] = '___name___';
+        $patterns[] = '___content___';
+        $patterns[] = '___time___';
+        $replacements[] = $cmt->avatar;
+        $replacements[] = fullname($cmt);
+        $replacements[] = $cmt->content;
+        $replacements[] = userdate($cmt->timecreated);
+
+        // use html template to format a single comment.
+        return str_replace($patterns, $replacements, $this->template);
+    }
+}
+
index 292e34c593a599f8ab5ea4d2349d5e8a8ffd4254..760fb548086cec373cb1c9052a294792bd468467 100644 (file)
@@ -1332,6 +1332,44 @@ $moodle_capabilities = array(
             'editingteacher' => CAP_ALLOW,
             'coursecreator' => CAP_ALLOW
         )
+    ),
+    'moodle/comment:view' => array(
+
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'user' => CAP_ALLOW,
+            'student' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'coursecreator' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        )
+    ),
+    'moodle/comment:post' => array(
+
+        'riskbitmask' => RISK_SPAM | RISK_PERSONAL,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'user' => CAP_ALLOW,
+            'student' => CAP_ALLOW,
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'coursecreator' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        )
+    ),
+    'moodle/comment:delete' => array(
+
+        'riskbitmask' => RISK_DATALOSS,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+            'editingteacher' => CAP_ALLOW,
+            'coursecreator' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        )
     )
 );
 
index 6188bb2709f7290376eb31929b46a4a467b22484..6ba9fbbe7848842a04a1b3ccf4fca5658f7346a9 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20090613" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20090719" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <INDEX NAME="parentcontextid-showinsubcontexts-pagetypepattern-subpagepattern" UNIQUE="false" FIELDS="parentcontextid, showinsubcontexts, pagetypepattern, subpagepattern"/>
       </INDEXES>
     </TABLE>
-    <TABLE NAME="block_positions" COMMENT="Stores the position of a sticky block_instance on a another page than the one where it was added." PREVIOUS="block_instances">
+    <TABLE NAME="block_positions" COMMENT="Stores the position of a sticky block_instance on a another page than the one where it was added." PREVIOUS="block_instances" NEXT="comments">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="blockinstanceid"/>
         <FIELD NAME="blockinstanceid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The block_instance this position relates to." PREVIOUS="id" NEXT="contextid"/>
         <INDEX NAME="blockinstanceid-contextid-pagetype-subpage" UNIQUE="true" FIELDS="blockinstanceid, contextid, pagetype, subpage"/>
       </INDEXES>
     </TABLE>
+    <TABLE NAME="comments" COMMENT="moodle comments module" PREVIOUS="block_positions">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="contextid"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="commentarea"/>
+        <FIELD NAME="commentarea" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="itemid"/>
+        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="commentarea" NEXT="content"/>
+        <FIELD NAME="content" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="itemid" NEXT="format"/>
+        <FIELD NAME="format" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="content" NEXT="userid"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="format" NEXT="timecreated"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="userid"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+    </TABLE>
   </TABLES>
 </XMLDB>
\ No newline at end of file
index 8371ff076f23fcdd232e105410a08eedeb5a0ceb..2976fd8aeae9cb15e59a732ef0b6acc25b574b51 100644 (file)
@@ -2322,6 +2322,33 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
         upgrade_main_savepoint($result, 2009071300);
     }
 
+    if ($result && $oldversion < 2009072400) {
+
+    /// Define table comments to be created
+        $table = new xmldb_table('comments');
+
+    /// Adding fields to table comments
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('commentarea', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('content', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('format', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+
+    /// Adding keys to table comments
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+    /// Conditionally launch create table for comments
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2009072400);
+    }
+
     return $result;
 }
 
index fe0da0c29e4a82b0bbea70f340043747bc548222..3ecb2780525f59deee03723fd45aec5505f38bf1 100644 (file)
@@ -1084,3 +1084,21 @@ function cancel_scroll_to_end() {
 function create_UFO_object(eid) {
     UFO.create(FO, eid);
 }
+function build_querystring(obj) {
+    if (typeof obj !== 'object') {
+        return null;
+    }
+    var list = [];
+    for(var k in obj) {
+        k = encodeURIComponent(k);
+        var value = obj[k];
+        if(obj[k] instanceof Array) {
+            for(var i in value) {
+                list.push(k+'[]='+encodeURIComponent(value[i]));
+            }
+        } else {
+            list.push(k+'='+encodeURIComponent(value));
+        }
+    }
+    return list.join('&');
+}
index f9316936ebcaa7e9ddc56fc099258d16c0147777..2fb565daae129087401cfee6549a40008f37d79d 100644 (file)
@@ -307,6 +307,8 @@ define('FEATURE_MOD_SUBPLUGINS', 'mod_subplugins');
 /** True if module has default completion */
 define('FEATURE_MODEDIT_DEFAULT_COMPLETION', 'modedit_default_completion');
 
+define('FEATURE_COMMENT', 'comment');
+
 
 /// PARAMETER HANDLING ////////////////////////////////////////////////////
 
@@ -6881,6 +6883,68 @@ function get_list_of_plugins($directory='mod', $exclude='', $basedir='') {
     return $plugins;
 }
 
+
+/**
+ * invoke plugin's callback functions
+ *
+ * @param string $type Plugin type e.g. 'mod'
+ * @param string $name Plugin name
+ * @param string $feature Feature code (FEATURE_xx constant)
+ * @param string $action Feature's action
+ * @param string $options parameters of callback function, should be an array
+ * @param mixed $default default value if callback function hasn't been defined
+ * @return mixed
+ */
+function plugin_callback($type, $name, $feature, $action, $options = null, $default=null) {
+    global $CFG;
+
+    $name = clean_param($name, PARAM_SAFEDIR);
+    $function = $name.'_'.$feature.'_'.$action;
+
+    switch($type) {
+        case 'mod' :
+            $file = $CFG->dirroot.'/mod/'.$name.'/lib.php';
+            break;
+        case 'block' :
+            // load block_base class
+            require_once($CFG->dirroot . '/blocks/moodleblock.class.php');
+            // block uses class based callback functions
+            // see blocks/moodleblock.class.php
+            $file = $CFG->dirroot.'/blocks/'.$name.'/block_'.$name.'.php';
+            $function = array('block_' . $name, 'comment_'.$action);
+            break;
+        case 'blog':
+            $file = $CFG->dirroot.'/blog/lib.php';
+            break;
+        case 'moodle':
+            // for special plugins, such as blog and tag
+            $file = $CFG->dirroot.'/'.$name.'/lib.php';
+            break;
+        default:
+            throw new Exception('Unsupported callback type ('.$type.')');
+    }
+
+    // Load library and look for function
+    if (file_exists($file)) {
+        require_once($file);
+    }
+    if (is_array($function) || function_exists($function)) {
+        // Function exists, so just return function result
+        $ret = call_user_func_array($function, $options);
+        if (is_null($ret)) {
+            return $default;
+        } else {
+            return $ret;
+        }
+    } else {
+        switch($feature) {
+            // If some features can also be checked in other ways
+            // for legacy support, this could be added here
+            default: return $default;
+        }
+    }
+}
+
 /**
  * Checks whether a plugin supports a specified feature.
  *
index 71a9dd03d3f454c2f6a63cbcbc835718dfa81107..e4d57b34db5d80053594c3cf14fdb8b87a3f2477 100644 (file)
@@ -1408,6 +1408,14 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL
     if ($text === '') {
         return ''; // no need to do any filters and cleaning
     }
+    if (!empty($options->comments) && !empty($CFG->usecomments)) {
+        require_once($CFG->libdir . '/commentlib.php');
+        $comment = new comment($options->comments);
+        $cmt = $comment->init(true);
+    } else {
+        $cmt = '';
+    }
+
 
     if (!isset($options->trusted)) {
         $options->trusted = false;
@@ -1455,7 +1463,7 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL
         $md5key = md5($hashstr);
         if (CLI_SCRIPT) {
             if (isset($croncache[$md5key])) {
-                return $croncache[$md5key];
+                return $croncache[$md5key].$cmt;
             }
         }
 
@@ -1469,7 +1477,7 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL
                     }
                     $croncache[$md5key] = $oldcacheitem->formattedtext;
                 }
-                return $oldcacheitem->formattedtext;
+                return $oldcacheitem->formattedtext.$cmt;
             }
         }
     }
@@ -1538,7 +1546,7 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL
                 unset($croncache[$key]);
             }
             $croncache[$md5key] = $text;
-            return $text;
+            return $text.$cmt;
         }
 
         $newcacheitem = new object();
@@ -1566,8 +1574,8 @@ function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL
             }
         }
     }
-
-    return $text;
+    return $text.$cmt;
+                
 }
 
 /**
index 2edc42babd1c5d2ad9875e0435d9b0e5da22ad26..6996010a9a7311fdcbd3cd5401a73fb2979a7b4b 100644 (file)
@@ -5494,3 +5494,77 @@ wikiadminactions {
     padding-left: 7px;
     float: left;
 }
+/**
+ * comments 2.0
+ *
+ */
+.comment-ctrl {
+    font-size: 12px;
+    display: none;
+    margin:0;
+    padding:0;
+}
+.comment-ctrl h5 {
+    margin:0;
+    padding: 5px;
+}
+
+.comment-area {
+    padding: 5px;
+}
+.comment-area .bd{
+}
+.comment-area textarea{
+    width:100%;
+    border: 2px solid grey;
+    overflow:auto;
+}
+
+.comment-area .fd{
+    text-align:right;
+}
+.comment-meta span{
+    color:grey
+}
+
+.comment-list-wrapper {
+}
+
+.comment-list {
+    /*height: 150px;*/
+    font-size: 11px;
+    overflow:auto;
+    list-style:none;
+    padding:0;
+    margin:0;
+}
+.comment-list li{
+    background: #ECEFF5;
+    margin: 5px;
+    clear:both;
+}
+
+.comment-paging{
+    /*background: #d8d8da;*/
+    text-align:center;
+}
+.comment-paging .pageno{
+    padding: 2px;
+}
+.comment-userpicture {
+    width: 45px;
+    float:left;
+}
+.comment-userpicture img.userpicture{
+    width: 35px;
+    height: 35px;
+}
+.comment-content{
+    margin-left: 50px;
+}
+.comment-container {
+    float:left;
+    width: 305px;
+    margin: 4px;
+}
+
index 8c6d571bbd97bc598be8347c3405f56141780ba7..0276755c17fb5c5803b97bd9c79808ed9f4a9edf 100644 (file)
@@ -6,7 +6,7 @@
 // This is compared against the values stored in the database to determine
 // whether upgrades should be performed (see lib/db/*.php)
 
-    $version = 2009071300;  // YYYYMMDD   = date of the last version bump
+    $version = 2009072400;  // YYYYMMDD   = date of the last version bump
                             //         XX = daily increments
 
     $release = '2.0 dev (Build: 20090724)';  // Human-friendly version name