MDL-65032 mod_forum: Add discussion locking functionality

This commit is contained in:
Peter 2019-03-14 14:00:33 +08:00
parent 082740b7c6
commit 2893812eb0
28 changed files with 435 additions and 23 deletions

View File

@ -0,0 +1 @@
define(["jquery","core/templates","core/notification","mod_forum/repository","mod_forum/selectors"],function(a,b,c,d,e){var f=function(f){f.on("click",e.lock.toggle,function(e){var f=a(this),g=f.data("forumid"),h=f.data("discussionid"),i=f.data("state");d.setDiscussionLockState(g,h,i).then(function(a){return b.render("mod_forum/discussion_lock_toggle",a)}).then(function(a,c){return b.replaceNode(f,a,c)})["catch"](c.exception),e.preventDefault()})};return{init:function(a){f(a)}}});

View File

@ -1 +1 @@
define(["core/ajax"],function(a){var b=function(b,c,d){var e={methodname:"mod_forum_set_subscription_state",args:{forumid:b,discussionid:c,targetstate:d}};return a.call([e])[0]},c=function(b,c,d){var e={methodname:"mod_forum_add_discussion_post",args:{postid:b,message:d,subject:c}};return a.call([e])[0]};return{setDiscussionSubscriptionState:b,addDiscussionPost:c}});
define(["core/ajax"],function(a){var b=function(b,c,d){var e={methodname:"mod_forum_set_subscription_state",args:{forumid:b,discussionid:c,targetstate:d}};return a.call([e])[0]},c=function(b,c,d){var e={methodname:"mod_forum_add_discussion_post",args:{postid:b,message:d,subject:c}};return a.call([e])[0]},d=function(b,c,d){var e={methodname:"mod_forum_set_lock_state",args:{forumid:b,discussionid:c,targetstate:d}};return a.call([e])[0]};return{setDiscussionSubscriptionState:b,addDiscussionPost:c,setDiscussionLockState:d}});

View File

@ -1 +1 @@
define([],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][data-action='toggle']"},pin:{toggle:".pindiscussion [data-action='toggle']"},post:{post:'[data-region="post"]',action:'[data-region="post-action"]',actionsContainer:'[data-region="post-actions-container"]',forumCoreContent:"[data-region-content='forum-post-core']",forumContent:"[data-content='forum-post']",forumSubject:"[data-region-content='forum-post-core-subject']",inpageReplyLink:"[data-action='collapsible-link']",inpageReplyContent:"[data-content='inpage-reply-content']",inpageReplyForm:"form[data-content='inpage-reply-form']",inpageSubmitBtn:"[data-action='forum-inpage-submit']",repliesContainer:"[data-region='replies-container']",modeSelect:"select[name='mode']"}}});
define([],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][data-action='toggle']"},pin:{toggle:".pindiscussion [data-action='toggle']"},post:{post:'[data-region="post"]',action:'[data-region="post-action"]',actionsContainer:'[data-region="post-actions-container"]',forumCoreContent:"[data-region-content='forum-post-core']",forumContent:"[data-content='forum-post']",forumSubject:"[data-region-content='forum-post-core-subject']",inpageReplyLink:"[data-action='collapsible-link']",inpageReplyContent:"[data-content='inpage-reply-content']",inpageReplyForm:"form[data-content='inpage-reply-form']",inpageSubmitBtn:"[data-action='forum-inpage-submit']",repliesContainer:"[data-region='replies-container']",modeSelect:"select[name='mode']"},lock:{toggle:"[data-action='toggle'][data-type='lock-toggle']"}}});

View File

@ -0,0 +1,68 @@
// 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/>.
/**
* Handle the manual locking of individual discussions
*
* @module mod_forum/lock_toggle
* @package mod_forum
* @copyright 2019 Peter Dias <peter@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'jquery',
'core/templates',
'core/notification',
'mod_forum/repository',
'mod_forum/selectors',
], function(
$,
Templates,
Notification,
Repository,
Selectors
) {
/**
* Register event listeners for the subscription toggle.
*
* @param {object} root The discussion list root element
*/
var registerEventListeners = function(root) {
root.on('click', Selectors.lock.toggle, function(e) {
var toggleElement = $(this);
var forumId = toggleElement.data('forumid');
var discussionId = toggleElement.data('discussionid');
var state = toggleElement.data('state');
Repository.setDiscussionLockState(forumId, discussionId, state)
.then(function(context) {
return Templates.render('mod_forum/discussion_lock_toggle', context);
})
.then(function(html, js) {
return Templates.replaceNode(toggleElement, html, js);
})
.catch(Notification.exception);
e.preventDefault();
});
};
return {
init: function(root) {
registerEventListeners(root);
}
};
});

View File

@ -14,7 +14,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Forum repository class to encapsulate all of the AJAX requests that
* Forum repository class to encapsulate all of the AJAX requests thatsubscribe or unsubscribe
* can be sent for forum.
*
* @module mod_forum/repository
@ -56,8 +56,21 @@ define(['core/ajax'], function(Ajax) {
return Ajax.call([request])[0];
};
var setDiscussionLockState = function(forumId, discussionId, targetState) {
var request = {
methodname: 'mod_forum_set_lock_state',
args: {
forumid: forumId,
discussionid: discussionId,
targetstate: targetState
}
};
return Ajax.call([request])[0];
};
return {
setDiscussionSubscriptionState: setDiscussionSubscriptionState,
addDiscussionPost: addDiscussionPost
addDiscussionPost: addDiscussionPost,
setDiscussionLockState: setDiscussionLockState
};
});

View File

@ -41,7 +41,10 @@ define([], function() {
inpageReplyForm: "form[data-content='inpage-reply-form']",
inpageSubmitBtn: "[data-action='forum-inpage-submit']",
repliesContainer: "[data-region='replies-container']",
modeSelect: "select[name='mode']",
modeSelect: "select[name='mode']"
},
lock: {
toggle: "[data-action='toggle'][data-type='lock-toggle']",
}
};
});

View File

@ -57,7 +57,8 @@ class discussion {
'usermodified' => $discussion->get_user_modified(),
'timestart' => $discussion->get_time_start(),
'timeend' => $discussion->get_time_end(),
'pinned' => $discussion->is_pinned()
'pinned' => $discussion->is_pinned(),
'locked' => $discussion->get_locked()
];
}, $discussions);
}

View File

@ -61,6 +61,8 @@ class discussion {
private $timeend;
/** @var bool $pinned Is the discussion pinned? */
private $pinned;
/** @var int $locked The timestamp of when the discussion was locked */
private $timelocked;
/**
* Constructor.
@ -78,6 +80,7 @@ class discussion {
* @param int $timestart Start time for the discussion
* @param int $timeend End time for the discussion
* @param bool $pinned Is the discussion pinned?
* @param int $locked Time this discussion was locked
*/
public function __construct(
int $id,
@ -92,7 +95,8 @@ class discussion {
int $usermodified,
int $timestart,
int $timeend,
bool $pinned
bool $pinned,
int $locked
) {
$this->id = $id;
$this->courseid = $courseid;
@ -107,6 +111,7 @@ class discussion {
$this->timestart = $timestart;
$this->timeend = $timeend;
$this->pinned = $pinned;
$this->timelocked = $locked;
}
/**
@ -228,6 +233,34 @@ class discussion {
return $this->pinned;
}
/**
* Check if this discussion is pinned.
*
* @return bool
*/
public function get_locked() : int {
return $this->timelocked;
}
/**
* Is this discussion locked based on it's locked attribute
*
* @return bool
*/
public function is_locked() : bool {
return ($this->timelocked ? true : false);
}
/**
* Set the locked timestamp
*
* @param int $timestamp
*/
public function toggle_locked_state(int $timestamp) {
// If it is locked already then unlock else set it to the timestamp.
$this->timelocked = ($this->timelocked ? 0 : $timestamp);
}
/**
* Check if the given post is the first post in this discussion.
*

View File

@ -545,6 +545,10 @@ class forum {
* @return bool
*/
public function is_discussion_locked(discussion_entity $discussion) : bool {
if ($discussion->is_locked()) {
return true;
}
if (!$this->has_lock_discussions_after()) {
return false;
}

View File

@ -88,11 +88,13 @@ class discussion extends exporter {
'modified' => ['type' => PARAM_INT],
'start' => ['type' => PARAM_INT],
'end' => ['type' => PARAM_INT],
'locked' => ['type' => PARAM_INT],
],
],
'userstate' => [
'type' => [
'subscribed' => ['type' => PARAM_BOOL],
'locked' => ['type' => PARAM_BOOL],
],
],
'capabilities' => [
@ -100,7 +102,8 @@ class discussion extends exporter {
'subscribe' => ['type' => PARAM_BOOL],
'move' => ['type' => PARAM_BOOL],
'pin' => ['type' => PARAM_BOOL],
'post' => ['type' => PARAM_BOOL]
'post' => ['type' => PARAM_BOOL],
'manage' => ['type' => PARAM_BOOL],
]
],
'urls' => [
@ -186,15 +189,18 @@ class discussion extends exporter {
'modified' => $discussion->get_time_modified(),
'start' => $discussion->get_time_start(),
'end' => $discussion->get_time_end(),
'locked' => $discussion->get_locked()
],
'userstate' => [
'subscribed' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord, $discussion->get_id()),
'locked' => $discussion->is_locked()
],
'capabilities' => [
'subscribe' => $capabilitymanager->can_subscribe_to_discussion($user, $discussion),
'move' => $capabilitymanager->can_move_discussion($user, $discussion),
'pin' => $capabilitymanager->can_pin_discussion($user, $discussion),
'post' => $capabilitymanager->can_post_in_discussion($user, $discussion)
'post' => $capabilitymanager->can_post_in_discussion($user, $discussion),
'manage' => $capabilitymanager->can_manage_forum($user)
],
'urls' => [
'view' => $urlfactory->get_discussion_view_url_from_discussion($discussion)->out(false),

View File

@ -126,7 +126,8 @@ class entity {
$record->usermodified,
$record->timestart,
$record->timeend,
$record->pinned
$record->pinned,
$record->timelocked
);
}

View File

@ -201,6 +201,10 @@ class discussion {
$exporteddiscussion['html']['pindiscussion'] = $this->get_pin_discussion_html();
}
if ($capabilities['manage']) {
$exporteddiscussion['html']['lockdiscussion'] = $this->get_lock_discussion_button_html();
}
return $this->renderer->render_from_template('mod_forum/forum_discussion', $exporteddiscussion);
}
@ -274,6 +278,24 @@ class discussion {
return $html;
}
/**
* Get the HTML to render the subscription button.
*
* @return string
*/
private function get_lock_discussion_button_html() : string {
global $PAGE;
$forumrecord = $this->forumrecord;
$discussionrecord = $this->discussionrecord;
$html = html_writer::div(
forum_get_lock_discussion_icon($forumrecord, $discussionrecord, null, true),
'discussionlock'
);
return $html;
}
/**
* Get the HTML to render the move discussion selector and button.
*

View File

@ -125,4 +125,20 @@ class discussion extends db_table_vault {
return $this->get_db()->count_records(self::TABLE, [
'forum' => $forum->get_id()]);
}
/**
* Update the discussion
*
* @param discussion_entity $discussion
* @return discussion_entity|null
*/
public function update_discussion($discussion) : ?discussion_entity {
if ($this->get_db()->update_record('forum_discussions', $discussion)) {
$records = $this->transform_db_records_to_entities([$discussion]);
return count($records) ? array_shift($records) : null;
}
return null;
}
}

View File

@ -56,6 +56,7 @@
<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"/>
<FIELD NAME="timelocked" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View File

@ -134,4 +134,14 @@ $functions = array(
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'mod_forum_set_lock_state' => array(
'classname' => 'mod_forum_external',
'methodname' => 'set_lock_state',
'classpath' => 'mod/forum/externallib.php',
'description' => 'Set the lock state for the discussion',
'type' => 'write',
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
);

View File

@ -114,7 +114,6 @@ function xmldb_forum_upgrade($oldversion) {
$dbman->add_field($table, $field);
}
// Forum savepoint reached.
upgrade_mod_savepoint(true, 2019031200, 'forum');
}
@ -138,9 +137,22 @@ function xmldb_forum_upgrade($oldversion) {
$dbman->add_field($table, $field);
}
// Forum savepoint reached.
upgrade_mod_savepoint(true, 2019040400, 'forum');
}
if ($oldversion < 2019040401) {
// Define field deleted to be added to forum_posts.
$table = new xmldb_table('forum_discussions');
$field = new xmldb_field('timelocked', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'pinned');
// Conditionally launch add field deleted.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Forum savepoint reached.
upgrade_mod_savepoint(true, 2019040401, 'forum');
}
return true;
}

View File

@ -636,7 +636,7 @@ class mod_forum_external extends external_api {
$discussion->messageinlinefiles = $messageinlinefiles;
}
$discussion->locked = forum_discussion_is_locked($forum, $discussion);
$discussion->timelocked = forum_discussion_is_locked($forum, $discussion);
$discussion->canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
if (forum_is_author_hidden($discussion, $forum)) {
@ -1227,6 +1227,7 @@ class mod_forum_external extends external_api {
$discussion->name = $discussion->subject;
$discussion->timestart = 0;
$discussion->timeend = 0;
$discussion->timelocked = 0;
$discussion->attachments = $options['attachmentsid'];
if (has_capability('mod/forum:pindiscussions', $context) && $options['discussionpinned']) {
@ -1510,4 +1511,75 @@ class mod_forum_external extends external_api {
public static function set_subscription_state_returns() {
return \mod_forum\local\exporters\discussion::get_read_structure();
}
/**
* Set the lock state.
*
* @param int $forumid
* @param int $discussionid
* @param string $targetstate
* @return \stdClass
*/
public static function set_lock_state($forumid, $discussionid, $targetstate) {
global $DB, $PAGE, $USER;
$params = self::validate_parameters(self::set_lock_state_parameters(), [
'forumid' => $forumid,
'discussionid' => $discussionid,
'targetstate' => $targetstate
]);
$vaultfactory = mod_forum\local\container::get_vault_factory();
$forumvault = $vaultfactory->get_forum_vault();
$forum = $forumvault->get_from_id($params['forumid']);
// If the targetstate(currentstate) is not 0 then it should be set to the current time.
$targetstate = $targetstate ? 0 : time();
self::validate_context($forum->get_context());
$managerfactory = mod_forum\local\container::get_manager_factory();
$capabilitymanager = $managerfactory->get_capability_manager($forum);
$discussionvault = $vaultfactory->get_discussion_vault();
$discussion = $discussionvault->get_from_id($params['discussionid']);
$legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
$forumrecord = $legacydatamapperfactory->get_forum_data_mapper()->to_legacy_object($forum);
// If the current state doesn't equal the desired state then update the current
// state to the desired state.
if ($capabilitymanager->can_manage_forum($USER)) {
$discussion->toggle_locked_state($targetstate);
$discussionrecord = $legacydatamapperfactory->get_discussion_data_mapper()->to_legacy_object($discussion);
$response = $discussionvault->update_discussion($discussionrecord);
$discussion = !$response ? $response : $discussion;
}
$exporterfactory = mod_forum\local\container::get_exporter_factory();
$exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion);
return $exporter->export($PAGE->get_renderer('mod_forum'));
}
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function set_lock_state_parameters() {
return new external_function_parameters(
[
'forumid' => new external_value(PARAM_INT, 'Forum that the discussion is in'),
'discussionid' => new external_value(PARAM_INT, 'The discussion to lock / unlock'),
'targetstate' => new external_value(PARAM_INT, 'The timestamp for the lock state')
]
);
}
/**
* Returns description of method result value.
*
* @return external_description
*/
public static function set_lock_state_returns() {
return \mod_forum\local\exporters\discussion::get_read_structure();
}
}

View File

@ -85,6 +85,8 @@ $string['cannotupdatepost'] = 'You can not update this post';
$string['cannotviewpostyet'] = 'You cannot read other students questions in this discussion yet because you haven\'t posted';
$string['cannotviewusersposts'] = 'There are no posts made by this user that you are able to view.';
$string['cleanreadtime'] = 'Mark old posts as read hour';
$string['clicktolockdiscussion'] = 'Click to lock this discussion';
$string['clicktounlockdiscussion'] = 'Click to unlock this discussion';
$string['clicktounsubscribe'] = 'You are subscribed to this discussion. Click to unsubscribe.';
$string['clicktosubscribe'] = 'You are not subscribed to this discussion. Click to subscribe.';
$string['completiondiscussions'] = 'Student must create discussions:';
@ -313,6 +315,7 @@ $string['lockdiscussionafter_help'] = 'Discussions may be automatically locked a
Users with the capability to reply to locked discussions can unlock a discussion by replying to it.';
$string['longpost'] = 'Long post';
$string['locked'] = 'Locked';
$string['mailnow'] = 'Send forum post notifications with no editing-time delay';
$string['manydiscussions'] = 'Discussions per page';
$string['managesubscriptionsoff'] = 'Finish managing subscriptions';
@ -403,6 +406,7 @@ $string['notexists'] = 'Discussion no longer exists';
$string['nothingnew'] = 'Nothing new for {$a}';
$string['notingroup'] = 'Sorry, but you need to be part of a group to see this forum.';
$string['notinstalled'] = 'The forum module is not installed';
$string['notlocked'] = 'Lock';
$string['notpartofdiscussion'] = 'This post is not part of a discussion!';
$string['notrackforum'] = 'Don\'t track unread posts';
$string['noviewdiscussionspermission'] = 'You do not have the permission to view discussions in this forum';

View File

@ -1822,8 +1822,9 @@ function forum_get_discussions($cm, $forumsort="", $fullpost=true, $unused=-1, $
$updatedsincesql = 'AND d.timemodified > ?';
$params[] = $updatedsince;
}
$discussionfields = "d.id as discussionid, d.course, d.forum, d.name, d.firstpost, d.groupid, d.assessed," .
" d.timemodified, d.usermodified, d.timestart, d.timeend, d.pinned";
$discussionfields = "d.id as discussionid, d.course, d.forum, d.name, d.firstpost, d.userid, d.groupid, d.assessed," .
" d.timemodified, d.usermodified, d.timestart, d.timeend, d.pinned, d.locked";
$allnames = get_all_user_name_fields(true, 'u');
$sql = "SELECT $postdata, $discussionfields,
@ -2582,6 +2583,71 @@ function forum_print_discussion_header(&$post, $forum, $group = -1, $datestring
}
/**
* Return the markup for the discussion lock toggling icon.
* @param stdClass $forum forum record
* @param stdClass $discussion discussion record
* @param null $returnurl the return url to use
* @param bool $includetext Whether or not to include the text with the icon
* @return string
* @throws coding_exception
* @throws moodle_exception
*/
function forum_get_lock_discussion_icon($forum, $discussion, $returnurl = null, $includetext = false) {
global $USER, $OUTPUT, $PAGE;
if ($returnurl === null && $PAGE->url) {
$returnurl = $PAGE->url->out();
}
$o = '';
$discussionid = $discussion->id;
$lockstatus = forum_discussion_is_locked($forum, $discussion);
$subscriptionlink = new moodle_url('/mod/forum/lockdiscussion.php', array(
'sesskey' => sesskey(),
'id' => $forum->id,
'd' => $discussion->id,
'returnurl' => $returnurl,
));
if ($includetext) {
$o .= $lockstatus ? get_string('locked', 'mod_forum') : get_string('notlocked', 'mod_forum');
}
if ($lockstatus) {
$output = $OUTPUT->pix_icon('t/unlock', get_string('clicktounlockdiscussion', 'forum'), 'core');
if ($includetext) {
$output .= get_string('locked', 'mod_forum');
}
return html_writer::link($subscriptionlink, $output, array(
'title' => get_string('clicktounlockdiscussion', 'forum'),
'class' => 'iconsmall',
'data-forumid' => $forum->id,
'data-discussionid' => $discussionid,
'data-action' => 'toggle',
'data-type' => 'lock-toggle',
'data-state' => $discussion->locked
));
} else {
$output = $OUTPUT->pix_icon('t/lock', get_string('clicktolockdiscussion', 'forum'), 'core');
if ($includetext) {
$output .= get_string('notlocked', 'mod_forum');
}
return html_writer::link("#", $output, array(
'title' => get_string('clicktolockdiscussion', 'forum'),
'class' => 'iconsmall',
'data-forumid' => $forum->id,
'data-discussionid' => $discussionid,
'data-action' => 'toggle',
'data-type' => 'lock-toggle',
'data-state' => $discussion->locked
));
}
}
/**
* Return the markup for the discussion subscription toggling icon.
*

View File

@ -968,6 +968,7 @@ if ($mformpost->is_cancelled()) {
$discussion = $fromform;
$discussion->name = $fromform->subject;
$discussion->timelocked = 0;
$newstopic = false;
if ($forum->type == 'news' && !$fromform->parent) {

View File

@ -301,13 +301,15 @@ span.unread {
background: url([[pix:mod_forum|t/unsubscribed]]) no-repeat -9999px -9999px;
}
.path-mod-forum .discussionsubscription {
.path-mod-forum .discussionsubscription,
.path-mod-forum .discussionlock {
margin-top: -10px;
text-align: right;
margin-bottom: 10px;
}
.path-mod-forum .discussionsubscription > a > img {
.path-mod-forum .discussionsubscription > a > img,
.path-mod-forum .discussionlock > a > img {
width: 12px;
padding: 0 4px;
}

View File

@ -0,0 +1,59 @@
{{!
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/>.
}}
{{!
@template mod_forum/discussion_lock_toggle
Template to display the discussion subscription toggle.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* TODO
Example context (json):
{
}
}}
{{#capabilities.manage}}
<a
class="iconsmall"
data-type="lock-toggle"
data-action="toggle"
data-discussionid="{{id}}"
data-forumid="{{forumid}}"
data-state="{{times.locked}}" href="#"
{{#userstate.locked}}
title="{{#str}}locked, forum{{/str}}"
{{/userstate.locked}}
{{^userstate.locked}}
title="{{#str}}notlocked, forum{{/str}}"
{{/userstate.locked}}
>
{{#userstate.locked}}
{{#pix}}t/unlock, core, {{#str}}clicktounlockdiscussion, forum{{/str}}{{/pix}}{{#str}}locked, forum{{/str}}
{{/userstate.locked}}
{{^userstate.locked}}
{{#pix}}t/lock, core, {{#str}}clicktolockdiscussion, forum{{/str}}{{/pix}}{{#str}}notlocked, forum{{/str}}
{{/userstate.locked}}
</a>
{{/capabilities.manage}}

View File

@ -32,7 +32,10 @@
<div id="discussion-container-{{uniqid}}" data-content="forum-discussion">
{{#html}}
{{{subscribe}}}
<div class="d-flex flex-wrap flex-row-reverse m-b-1" data-container="discussion-tools" style="text-align: right;">
<div class="pl-1">{{{lockdiscussion}}}</div>
<div class="pl-1">{{{subscribe}}}</div>
</div>
{{{neighbourlinks}}}
<div class="d-flex flex-wrap mb-1">
@ -52,10 +55,11 @@
{{#html.neighbourlinks}}{{{.}}}{{/html.neighbourlinks}}
</div>
{{#js}}
require(['jquery', 'mod_forum/discussion', 'mod_forum/posts_list'], function($, Discussion, PostsList) {
require(['jquery', 'mod_forum/discussion', 'mod_forum/posts_list', 'mod_forum/lock_toggle'], function($, Discussion, PostsList, LockToggle) {
var root = $("[data-content='forum-discussion']");
Discussion.init(root);
PostsList.init(root);
var root = $('#discussion-container-{{uniqid}}');
var root = $('[data-container="discussion-tools"]');
LockToggle.init(root);
});
{{/js}}

View File

@ -193,6 +193,10 @@ class mod_forum_generator extends testing_module_generator {
$record['pinned'] = FORUM_DISCUSSION_UNPINNED;
}
if (!isset($record['locked'])) {
$record['locked'] = "0";
}
if (isset($record['mailed'])) {
$mailed = $record['mailed'];
}

View File

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2019040400; // The current module version (Date: YYYYMMDDXX)
$plugin->version = 2019040401; // The current module version (Date: YYYYMMDDXX)
$plugin->requires = 2018112800; // Requires this Moodle version
$plugin->component = 'mod_forum'; // Full name of the plugin (used for diagnostics)

View File

@ -69,6 +69,7 @@ select {
thead .header th,
tbody .discussion td {
&.discussionlock,
&.discussionsubscription {
width: 16px;
padding-left: 0.5em;
@ -83,12 +84,14 @@ select {
}
.discussionsubscription,
.discussionlock,
.replies {
text-align: center;
}
.topic,
.discussionsubscription,
.discussionlock,
.topic.starter,
.replies,
.lastpost {

View File

@ -15023,7 +15023,8 @@ select {
.path-mod-forum .forumheaderlist thead .header.lastpost {
text-align: right; }
.path-mod-forum .forumheaderlist thead .header th.discussionsubscription,
.path-mod-forum .forumheaderlist thead .header th.discussionlock, .path-mod-forum .forumheaderlist thead .header th.discussionsubscription,
.path-mod-forum .forumheaderlist tbody .discussion td.discussionlock,
.path-mod-forum .forumheaderlist tbody .discussion td.discussionsubscription {
width: 16px;
padding-left: 0.5em;
@ -15034,11 +15035,13 @@ select {
white-space: normal; }
.path-mod-forum .forumheaderlist .discussion .discussionsubscription,
.path-mod-forum .forumheaderlist .discussion .discussionlock,
.path-mod-forum .forumheaderlist .discussion .replies {
text-align: center; }
.path-mod-forum .forumheaderlist .discussion .topic,
.path-mod-forum .forumheaderlist .discussion .discussionsubscription,
.path-mod-forum .forumheaderlist .discussion .discussionlock,
.path-mod-forum .forumheaderlist .discussion .topic.starter,
.path-mod-forum .forumheaderlist .discussion .replies,
.path-mod-forum .forumheaderlist .discussion .lastpost {

View File

@ -15280,7 +15280,8 @@ select {
.path-mod-forum .forumheaderlist thead .header.lastpost {
text-align: right; }
.path-mod-forum .forumheaderlist thead .header th.discussionsubscription,
.path-mod-forum .forumheaderlist thead .header th.discussionlock, .path-mod-forum .forumheaderlist thead .header th.discussionsubscription,
.path-mod-forum .forumheaderlist tbody .discussion td.discussionlock,
.path-mod-forum .forumheaderlist tbody .discussion td.discussionsubscription {
width: 16px;
padding-left: 0.5em;
@ -15291,11 +15292,13 @@ select {
white-space: normal; }
.path-mod-forum .forumheaderlist .discussion .discussionsubscription,
.path-mod-forum .forumheaderlist .discussion .discussionlock,
.path-mod-forum .forumheaderlist .discussion .replies {
text-align: center; }
.path-mod-forum .forumheaderlist .discussion .topic,
.path-mod-forum .forumheaderlist .discussion .discussionsubscription,
.path-mod-forum .forumheaderlist .discussion .discussionlock,
.path-mod-forum .forumheaderlist .discussion .topic.starter,
.path-mod-forum .forumheaderlist .discussion .replies,
.path-mod-forum .forumheaderlist .discussion .lastpost {