MDL-372 forum: support for pinned discussion topics
authorCharles Fulton <mackensen@gmail.com>
Sun, 25 Mar 2012 14:39:24 +0000 (07:39 -0700)
committerRyan Wyllie <ryan@moodle.com>
Wed, 13 Jan 2016 02:01:26 +0000 (02:01 +0000)
13 files changed:
mod/forum/backup/moodle2/backup_forum_stepslib.php
mod/forum/classes/event/discussion_pinned.php [new file with mode: 0644]
mod/forum/classes/event/discussion_unpinned.php [new file with mode: 0644]
mod/forum/db/access.php
mod/forum/db/install.xml
mod/forum/db/log.php
mod/forum/db/upgrade.php
mod/forum/discuss.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/styles.css
mod/forum/version.php

index c423af3..ed374a6 100644 (file)
@@ -51,7 +51,7 @@ class backup_forum_activity_structure_step extends backup_activity_structure_ste
         $discussion = new backup_nested_element('discussion', array('id'), array(
             'name', 'firstpost', 'userid', 'groupid',
             'assessed', 'timemodified', 'usermodified', 'timestart',
-            'timeend'));
+            'timeend', 'pinned'));
 
         $posts = new backup_nested_element('posts');
 
diff --git a/mod/forum/classes/event/discussion_pinned.php b/mod/forum/classes/event/discussion_pinned.php
new file mode 100644 (file)
index 0000000..1852201
--- /dev/null
@@ -0,0 +1,102 @@
+<?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/>.
+/**
+ * The mod_forum discussion pinned event.
+ *
+ * @package    mod_forum
+ * @copyright  2014 Charles Fulton <fultonc@lafayette.edu>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace mod_forum\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_forum discussion pinned event.
+ *
+ * @package    mod_forum
+ * @copyright  2014 Charles Fulton <fultonc@lafayette.edu>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class discussion_pinned extends \core\event\base {
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'forum_discussions';
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user {$this->userid} has pinned the discussion {$this->objectid} in the forum {$this->other['forumid']}";
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventdiscussionpinned', 'mod_forum');
+    }
+
+    /**
+     * Get URL related to the action
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/forum/discuss.php', array('d' => $this->objectid));
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        // The legacy log table expects a relative path to /mod/forum/.
+        $logurl = substr($this->get_url()->out_as_local_url(), strlen('/mod/forum/'));
+        return array($this->courseid, 'forum', 'pin discussion', $logurl, $this->objectid, $this->contextinstanceid);
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+        if (!isset($this->other['forumid'])) {
+            throw new \coding_exception('forumid must be set in $other.');
+        }
+        if ($this->contextlevel != CONTEXT_MODULE) {
+            throw new \coding_exception('Context passed must be module context.');
+        }
+        if (!isset($this->objectid)) {
+            throw new \coding_exception('objectid must be set to the discussionid.');
+        }
+    }
+}
diff --git a/mod/forum/classes/event/discussion_unpinned.php b/mod/forum/classes/event/discussion_unpinned.php
new file mode 100644 (file)
index 0000000..1e8064a
--- /dev/null
@@ -0,0 +1,102 @@
+<?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/>.
+/**
+ * The mod_forum discussion unpinned event.
+ *
+ * @package    mod_forum
+ * @copyright  2014 Charles Fulton <fultonc@lafayette.edu>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace mod_forum\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_forum discussion unpinned event.
+ *
+ * @package    mod_forum
+ * @copyright  2014 Charles Fulton <fultonc@lafayette.edu>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class discussion_unpinned extends \core\event\base {
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'forum_discussions';
+    }
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user {$this->userid} has unpinned the discussion {$this->objectid} in the forum {$this->other['forumid']}";
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventdiscussionunpinned', 'mod_forum');
+    }
+
+    /**
+     * Get URL related to the action
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/forum/discuss.php', array('d' => $this->objectid));
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        // The legacy log table expects a relative path to /mod/forum/.
+        $logurl = substr($this->get_url()->out_as_local_url(), strlen('/mod/forum/'));
+        return array($this->courseid, 'forum', 'unpin discussion', $logurl, $this->objectid, $this->contextinstanceid);
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+        if (!isset($this->other['forumid'])) {
+            throw new \coding_exception('forumid must be set in $other.');
+        }
+        if ($this->contextlevel != CONTEXT_MODULE) {
+            throw new \coding_exception('Context passed must be module context.');
+        }
+        if (!isset($this->objectid)) {
+            throw new \coding_exception('objectid must be set to the discussionid.');
+        }
+    }
+}
index 396a550..9fecdc6 100644 (file)
@@ -224,6 +224,17 @@ $capabilities = array(
         )
     ),
 
+    'mod/forum:pindiscussions' => array(
+
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        )
+    ),
+
     'mod/forum:editanypost' => array(
 
         'riskbitmask' => RISK_SPAM,
index 7353758..2bd2195 100644 (file)
@@ -52,6 +52,7 @@
         <FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+        <FIELD NAME="pinned" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index 22ae66c..5ac9f2d 100644 (file)
@@ -41,4 +41,6 @@ $logs = array(
     array('module'=>'forum', 'action'=>'view forum', 'mtable'=>'forum', 'field'=>'name'),
     array('module'=>'forum', 'action'=>'subscribe', 'mtable'=>'forum', 'field'=>'name'),
     array('module'=>'forum', 'action'=>'unsubscribe', 'mtable'=>'forum', 'field'=>'name'),
+    array('module'=>'forum', 'action'=>'pin discussion', 'mtable'=>'forum_discussions', 'field'=>'name'),
+    array('module'=>'forum', 'action'=>'unpin discussion', 'mtable'=>'forum_discussions', 'field'=>'name'),
 );
\ No newline at end of file
index 4edc4b6..3dcbc16 100644 (file)
@@ -239,6 +239,19 @@ function xmldb_forum_upgrade($oldversion) {
 
     // Moodle v2.8.0 release upgrade line.
     // Put any upgrade step following this.
+    if ($oldversion < 2014112400) {
+
+        // Add support for pinned discussions.
+        $table = new xmldb_table('forum_discussions');
+        $field = new xmldb_field('pinned', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'timeend');
+
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Forum savepoint reached.
+        upgrade_mod_savepoint(true, 2014112400, 'forum');
+    }
 
     // Moodle v2.9.0 release upgrade line.
     // Put any upgrade step following this.
index 33fe847..82c8e21 100644 (file)
@@ -32,6 +32,7 @@ $mode   = optional_param('mode', 0, PARAM_INT);          // If set, changes the
 $move   = optional_param('move', 0, PARAM_INT);          // If set, moves this discussion to another forum
 $mark   = optional_param('mark', '', PARAM_ALPHA);       // Used for tracking read posts if user initiated.
 $postid = optional_param('postid', 0, PARAM_INT);        // Used for tracking read posts if user initiated.
+$pin = optional_param('pin', -1, PARAM_INT);          // If set, pin or unpin this discussion.
 
 $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$d));
 if ($parent !== 0) {
@@ -172,6 +173,33 @@ if ($move > 0 and confirm_sesskey()) {
     redirect($return.'&move=-1&sesskey='.sesskey());
 }
 
+// Pin or unpin discussion if requested.
+if ($pin !== -1 && confirm_sesskey()) {
+    require_capability('mod/forum:pindiscussions', $modcontext);
+
+    $params = array('context' => $modcontext, 'objectid' => $discussion->id, 'other' => array('forumid' => $forum->id));
+
+    switch ($pin) {
+        case FORUM_DISCUSSION_PINNED:
+            $DB->set_field('forum_discussions', 'pinned', $pin, array('id' => $discussion->id));
+            $event = \mod_forum\event\discussion_pinned::create($params);
+            $event->add_record_snapshot('forum_discussions', $discussion);
+            $event->trigger();
+            break;
+        case FORUM_DISCUSSION_UNPINNED:
+            $DB->set_field('forum_discussions', 'pinned', $pin, array('id' => $discussion->id));
+            $event = \mod_forum\event\discussion_unpinned::create($params);
+            $event->add_record_snapshot('forum_discussions', $discussion);
+            $event->trigger();
+            break;
+        default:
+            echo $OUTPUT->notfication("Invalid value when attempting to pin/unpin discussion");
+            break;
+    }
+
+    redirect(new moodle_url('/mod/forum/discuss.php', array('d' => $discussion->id)));
+}
+
 // Trigger discussion viewed event.
 forum_discussion_view($modcontext, $forum, $discussion);
 
@@ -333,6 +361,19 @@ if ($forum->type != 'single'
     }
     echo "</div>";
 }
+
+if (has_capability('mod/forum:pindiscussions', $modcontext)) {
+    if ($discussion->pinned == FORUM_DISCUSSION_PINNED) {
+        $pinlink = FORUM_DISCUSSION_UNPINNED;
+        $pintext = get_string('discussionunpin', 'forum');
+    } else {
+        $pinlink = FORUM_DISCUSSION_PINNED;
+        $pintext = get_string('discussionpin', 'forum');
+    }
+    $button = new single_button(new moodle_url('discuss.php', array('pin' => $pinlink, 'd' => $discussion->id)), $pintext, 'post');
+    echo html_writer::tag('div', $OUTPUT->render($button), array('class' => 'discussioncontrol pindiscussion'));
+}
+
 echo '<div class="clearfloat">&nbsp;</div>';
 echo "</div>";
 
index aa23a28..951ff61 100644 (file)
@@ -138,6 +138,8 @@ $string['discussionmovedpost'] = 'This discussion has been moved to <a href="{$a
 $string['discussionname'] = 'Discussion name';
 $string['discussionnownotsubscribed'] = '{$a->name} will NOT be notified of new posts in \'{$a->discussion}\' of \'{$a->forum}\'';
 $string['discussionnowsubscribed'] = '{$a->name} will be notified of new posts in \'{$a->discussion}\' of \'{$a->forum}\'';
+$string['discussionpin'] = 'Pin';
+$string['discussionpinned'] = 'Pinned: {$a}';
 $string['discussionsubscribestop'] = 'I don\'t want to be notified of new posts in this discussion';
 $string['discussionsubscribestart'] = 'Send me notifications of new posts in this discussion';
 $string['discussionsubscription'] = 'Discussion subscription';
@@ -146,6 +148,7 @@ $string['discussions'] = 'Discussions';
 $string['discussionsstartedby'] = 'Discussions started by {$a}';
 $string['discussionsstartedbyrecent'] = 'Discussions recently started by {$a}';
 $string['discussionsstartedbyuserincourse'] = 'Discussions started by {$a->fullname} in {$a->coursename}';
+$string['discussionunpin'] = 'Unpin';
 $string['discussthistopic'] = 'Discuss this topic';
 $string['displayend'] = 'Display end';
 $string['displayend_help'] = 'This setting specifies whether a forum post should be hidden after a certain date. Note that administrators can always view forum posts.';
@@ -168,6 +171,8 @@ $string['eventdiscussionmoved'] = 'Discussion moved';
 $string['eventdiscussionviewed'] = 'Discussion viewed';
 $string['eventdiscussionsubscriptioncreated'] = 'Discussion subscription created';
 $string['eventdiscussionsubscriptiondeleted'] = 'Discussion subscription deleted';
+$string['eventdiscussionpinned'] = 'Discussion pinned';
+$string['eventdiscussionunpinned'] = 'Discussion unpinned';
 $string['eventuserreportviewed'] = 'User report viewed';
 $string['eventpostcreated'] = 'Post created';
 $string['eventpostdeleted'] = 'Post deleted';
@@ -229,6 +234,7 @@ $string['forum:exportpost'] = 'Export post';
 $string['forumintro'] = 'Description';
 $string['forum:managesubscriptions'] = 'Manage subscriptions';
 $string['forum:movediscussions'] = 'Move discussions';
+$string['forum:pindiscussions'] = 'Pin discussions';
 $string['forum:postwithoutthrottling'] = 'Exempt from post threshold';
 $string['forumname'] = 'Forum name';
 $string['forumposts'] = 'Forum posts';
index 29f4f6d..4dd0611 100644 (file)
@@ -69,6 +69,9 @@ if (!defined('FORUM_CRON_USER_CACHE')) {
  */
 define('FORUM_POSTS_ALL_USER_GROUPS', -2);
 
+define('FORUM_DISCUSSION_PINNED', 1);
+define('FORUM_DISCUSSION_UNPINNED', 0);
+
 /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
 
 /**
@@ -2593,7 +2596,7 @@ function forum_get_discussions($cm, $forumsort="", $fullpost=true, $unused=-1, $
     }
 
     $allnames = get_all_user_name_fields(true, 'u');
-    $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend, $allnames,
+    $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend, d.pinned,  $allnames,
                    u.email, u.picture, u.imagealt $umfields
               FROM {forum_discussions} d
                    JOIN {forum_posts} p ON p.discussion = d.id
@@ -2601,7 +2604,7 @@ function forum_get_discussions($cm, $forumsort="", $fullpost=true, $unused=-1, $
                    $umtable
              WHERE d.forum = ? AND p.parent = 0
                    $timelimit $groupselect
-          ORDER BY $forumsort";
+          ORDER BY pinned DESC, $forumsort";
     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
 }
 
@@ -2620,6 +2623,8 @@ function forum_get_discussions($cm, $forumsort="", $fullpost=true, $unused=-1, $
  * by the user, it simply uses it as a reference to find the neighbours. On the other hand,
  * the returned neighbours are checked and are accessible to the current user.
  *
+ * This function ignores whether a discussion is pinned.
+ *
  * @param object $cm The CM record.
  * @param object $discussion The discussion record.
  * @param object $forum The forum instance record.
@@ -3632,8 +3637,11 @@ function forum_print_discussion_header(&$post, $forum, $group = -1, $datestring
     echo "\n\n";
     echo '<tr class="discussion r'.$rowcount.$timedoutsidewindow.'">';
 
-    // Topic
-    echo '<td class="topic starter">';
+    $topicclass = 'topic starter';
+    if (FORUM_DISCUSSION_PINNED == $post->pinned) {
+        $topicclass .= ' pinned';
+    }
+    echo '<td class="'.$topicclass.'">';
 
     $canalwaysseetimedpost = $USER->id == $post->userid || $canviewhiddentimedposts;
     if ($timeddiscussion && $canalwaysseetimedpost) {
@@ -5426,8 +5434,11 @@ function forum_print_latest_discussions($course, $forum, $maxdiscussions = -1, $
         } else {
             $ownpost=false;
         }
-        // Use discussion name instead of subject of first post
+        // Use discussion name instead of subject of first post.
         $discussion->subject = $discussion->name;
+        if (FORUM_DISCUSSION_PINNED == $discussion->pinned) {
+            $discussion->subject = get_string('discussionpinned', 'forum', $discussion->subject);
+        }
 
         switch ($displayformat) {
             case 'header':
index 3b61658..9eea9ef 100644 (file)
@@ -852,6 +852,7 @@ if ($mform_post->is_cancelled()) {
         }
         $discussion->timestart = $fromform->timestart;
         $discussion->timeend = $fromform->timeend;
+        $discussion->pinned = FORUM_DISCUSSION_UNPINNED;
 
         $allowedgroups = array();
         $groupstopostto = array();
index 25874a6..4e37103 100644 (file)
@@ -86,6 +86,7 @@
 #page-mod-forum-discuss .discussioncontrol.displaymode {text-align:center;}
 #page-mod-forum-discuss .discussioncontrol.movediscussion {float:right;width:auto;text-align:right;padding-right:10px;}
 #page-mod-forum-discuss .discussioncontrol.movediscussion .movediscussionoption {}
+#page-mod-forum-discuss .discussioncontrol.pindiscussion {float:right;width:auto;text-align:right;padding-right:10px;}
 
 /** Styles for view.php **/
 #page-mod-forum-view .forumaddnew {margin-bottom: 20px;}
index 2b3ff68..4481b58 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015111601;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2015112300;       // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2015111000;       // Requires this Moodle version
 $plugin->component = 'mod_forum';      // Full name of the plugin (used for diagnostics)