Merge branch 'MDL-65033-master2' of git://github.com/peterRd/moodle
This commit is contained in:
Vendored
+5
-5
@@ -79,14 +79,16 @@ abstract class exporter {
|
||||
}
|
||||
|
||||
$missingdataerr = 'Exporter class is missing required related data: (' . get_called_class() . ') ';
|
||||
$scalartypes = ['string', 'int', 'bool', 'float'];
|
||||
$scalarcheck = 'is_' . $classname;
|
||||
|
||||
if ($nullallowed && array_key_exists($key, $related) && $related[$key] === null) {
|
||||
$this->related[$key] = $related[$key];
|
||||
if ($nullallowed && (!array_key_exists($key, $related) || $related[$key] === null)) {
|
||||
$this->related[$key] = null;
|
||||
|
||||
} else if ($isarray) {
|
||||
if (array_key_exists($key, $related) && is_array($related[$key])) {
|
||||
foreach ($related[$key] as $index => $value) {
|
||||
if (!$value instanceof $classname) {
|
||||
if (!$value instanceof $classname && !$scalarcheck($value)) {
|
||||
throw new coding_exception($missingdataerr . $key . ' => ' . $classname . '[]');
|
||||
}
|
||||
}
|
||||
@@ -96,8 +98,6 @@ abstract class exporter {
|
||||
}
|
||||
|
||||
} else {
|
||||
$scalartypes = ['string', 'int', 'bool', 'float'];
|
||||
$scalarcheck = 'is_' . $classname;
|
||||
if (array_key_exists($key, $related) &&
|
||||
((in_array($classname, $scalartypes) && $scalarcheck($related[$key])) ||
|
||||
($related[$key] instanceof $classname))) {
|
||||
|
||||
@@ -356,6 +356,7 @@ class icon_system_fontawesome extends icon_system_font {
|
||||
'core:t/edit' => 'fa-cog',
|
||||
'core:t/emailno' => 'fa-ban',
|
||||
'core:t/email' => 'fa-envelope-o',
|
||||
'core:t/emptystar' => 'fa-star-o',
|
||||
'core:t/enrolusers' => 'fa-user-plus',
|
||||
'core:t/expanded' => 'fa-caret-down',
|
||||
'core:t/go' => 'fa-play',
|
||||
|
||||
@@ -47,7 +47,8 @@ class core_exporter_testcase extends advanced_testcase {
|
||||
'context' => null,
|
||||
'aint' => 5,
|
||||
'astring' => 'valid string',
|
||||
'abool' => false
|
||||
'abool' => false,
|
||||
'ints' => []
|
||||
);
|
||||
$this->invalidrelated = array(
|
||||
'simplestdClass' => 'a string',
|
||||
@@ -55,7 +56,8 @@ class core_exporter_testcase extends advanced_testcase {
|
||||
'context' => null,
|
||||
'aint' => false,
|
||||
'astring' => 4,
|
||||
'abool' => 'not a boolean'
|
||||
'abool' => 'not a boolean',
|
||||
'ints' => null
|
||||
);
|
||||
|
||||
$this->validdata = array('stringA' => 'A string', 'stringAformat' => FORMAT_HTML, 'intB' => 4);
|
||||
@@ -118,6 +120,23 @@ class core_exporter_testcase extends advanced_testcase {
|
||||
$result = $exporter->export($output);
|
||||
}
|
||||
|
||||
public function test_invalid_related_all_cases() {
|
||||
global $PAGE;
|
||||
|
||||
foreach ($this->invalidrelated as $key => $value) {
|
||||
$data = $this->validrelated;
|
||||
$data[$key] = $value;
|
||||
|
||||
try {
|
||||
$exporter = new core_testable_exporter($this->validdata, $data);
|
||||
$output = $PAGE->get_renderer('core');
|
||||
$result = $exporter->export($output);
|
||||
} catch (coding_exception $e) {
|
||||
$this->assertNotFalse(strpos($e->getMessage(), $key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_valid_data_and_related() {
|
||||
global $PAGE;
|
||||
$output = $PAGE->get_renderer('core');
|
||||
@@ -196,7 +215,7 @@ class core_testable_exporter extends \core\external\exporter {
|
||||
protected static function define_related() {
|
||||
// We cache the context so it does not need to be retrieved from the course.
|
||||
return array('simplestdClass' => 'stdClass', 'arrayofstdClass' => 'stdClass[]', 'context' => 'context?',
|
||||
'astring' => 'string', 'abool' => 'bool', 'aint' => 'int');
|
||||
'astring' => 'string', 'abool' => 'bool', 'aint' => 'int', 'ints' => 'int[]');
|
||||
}
|
||||
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["mod_forum/subscription_toggle"],function(a){return{init:function(b){a.init(b)}}});
|
||||
define(["jquery","core/templates","core/str","core/notification","mod_forum/subscription_toggle","mod_forum/selectors","mod_forum/repository"],function(a,b,c,d,e,f,g){var h=function(e){e.on("click",f.favourite.toggle,function(){var b=a(this),c=b.data("forumid"),e=b.data("discussionid"),f=b.data("targetstate");g.setFavouriteDiscussionState(c,e,f).then(function(){return location.reload()})["catch"](d.exception)}),e.on("click",f.pin.toggle,function(b){b.preventDefault();var c=a(this),e=c.data("forumid"),f=c.data("discussionid"),h=c.data("targetstate");g.setPinDiscussionState(e,f,h).then(function(){return location.reload()})["catch"](d.exception)}),e.on("click",f.lock.toggle,function(e){var f=a(this),h=f.data("forumid"),i=f.data("discussionid"),j=f.data("state");g.setDiscussionLockState(h,i,j).then(function(a){return a.forumid=h,b.render("mod_forum/discussion_lock_toggle",a)}).then(function(a,c){return b.replaceNode(f,a,c)}).then(function(){return c.get_string("lockupdated","forum").done(function(a){return d.addNotification({message:a,type:"info"})})})["catch"](d.exception),e.preventDefault()})};return{init:function(a){e.init(a),h(a)}}});
|
||||
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/templates","core/notification","mod_forum/repository","mod_forum/selectors","core/str"],function(a,b,c,d,e,f){var g=function(g){g.on("click",e.favourite.toggle,function(e){var g=a(this),h=g.data("forumid"),i=g.data("discussionid"),j=g.data("targetstate");d.setFavouriteDiscussionState(h,i,j).then(function(a){return b.render("mod_forum/discussion_favourite_toggle",a)}).then(function(a,c){return b.replaceNode(g,a,c)}).then(function(){return f.get_string("favouriteupdated","forum").done(function(a){return c.addNotification({message:a,type:"info"})})})["catch"](c.exception),e.preventDefault()})};return{init:function(a){g(a)}}});
|
||||
+1
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax","core/str","core/templates","core/notification","mod_forum/repository","mod_forum/selectors","core/str"],function(a,b,c,d,e,f,g,h){var i=function(b){b.on("click",g.pin.toggle,function(b){var c=a(this),g=c.data("forumid"),i=c.data("discussionid"),j=c.data("targetstate");f.setPinDiscussionState(g,i,j).then(function(a){return d.render("mod_forum/discussion_pin_toggle",a)}).then(function(a,b){return d.replaceNode(c,a,b)}).then(function(){return h.get_string("pinupdated","forum").done(function(a){return e.addNotification({message:a,type:"info"})})}).fail(e.exception),b.preventDefault()})};return{init:function(a){i(a)}}});
|
||||
+1
-1
@@ -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]},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}});
|
||||
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_toggle_favourite_state",args:{discussionid:c,targetstate:d}};return a.call([e])[0]},e=function(b,c,d){var e={methodname:"mod_forum_set_lock_state",args:{forumid:b,discussionid:c,targetstate:d}};return a.call([e])[0]},f=function(b,c,d){var e={methodname:"mod_forum_set_pin_state",args:{discussionid:c,targetstate:d}};return a.call([e])[0]};return{setDiscussionSubscriptionState:b,addDiscussionPost:c,setDiscussionLockState:e,setFavouriteDiscussionState:d,setPinDiscussionState:f}});
|
||||
Vendored
+1
-1
@@ -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']"},lock:{toggle:"[data-action='toggle'][data-type='lock-toggle']"}}});
|
||||
define([],function(){return{subscription:{toggle:"[data-type='subscription-toggle'][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']"},favourite:{toggle:"[data-type='favorite-toggle'][data-action='toggle']"},pin:{toggle:"[data-type='pin-toggle'][data-action='toggle']"}}});
|
||||
@@ -21,10 +21,82 @@
|
||||
* @copyright 2019 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['mod_forum/subscription_toggle'], function(SubscriptionToggle) {
|
||||
define([
|
||||
'jquery',
|
||||
'core/templates',
|
||||
'core/str',
|
||||
'core/notification',
|
||||
'mod_forum/subscription_toggle',
|
||||
'mod_forum/selectors',
|
||||
'mod_forum/repository',
|
||||
], function(
|
||||
$,
|
||||
Templates,
|
||||
String,
|
||||
Notification,
|
||||
SubscriptionToggle,
|
||||
Selectors,
|
||||
Repository
|
||||
) {
|
||||
var registerEventListeners = function(root) {
|
||||
root.on('click', Selectors.favourite.toggle, function() {
|
||||
var toggleElement = $(this);
|
||||
var forumId = toggleElement.data('forumid');
|
||||
var discussionId = toggleElement.data('discussionid');
|
||||
var subscriptionState = toggleElement.data('targetstate');
|
||||
Repository.setFavouriteDiscussionState(forumId, discussionId, subscriptionState)
|
||||
.then(function() {
|
||||
return location.reload();
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
root.on('click', Selectors.pin.toggle, function(e) {
|
||||
e.preventDefault();
|
||||
var toggleElement = $(this);
|
||||
var forumId = toggleElement.data('forumid');
|
||||
var discussionId = toggleElement.data('discussionid');
|
||||
var state = toggleElement.data('targetstate');
|
||||
Repository.setPinDiscussionState(forumId, discussionId, state)
|
||||
.then(function() {
|
||||
return location.reload();
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
|
||||
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) {
|
||||
context.forumid = forumId;
|
||||
return Templates.render('mod_forum/discussion_lock_toggle', context);
|
||||
})
|
||||
.then(function(html, js) {
|
||||
return Templates.replaceNode(toggleElement, html, js);
|
||||
})
|
||||
.then(function() {
|
||||
return String.get_string('lockupdated', 'forum')
|
||||
.done(function(s) {
|
||||
return Notification.addNotification({
|
||||
message: s,
|
||||
type: "info"
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: function(root) {
|
||||
SubscriptionToggle.init(root);
|
||||
registerEventListeners(root);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 discussion subscription toggling on a discussion list in
|
||||
* the forum view.
|
||||
*
|
||||
* @module mod_forum/favourite_toggle
|
||||
* @package mod_forum
|
||||
* @copyright 2019 Peter Dias <[email protected]>
|
||||
* @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',
|
||||
'core/str',
|
||||
], function(
|
||||
$,
|
||||
Templates,
|
||||
Notification,
|
||||
Repository,
|
||||
Selectors,
|
||||
String
|
||||
) {
|
||||
|
||||
/**
|
||||
* Register event listeners for the subscription toggle.
|
||||
*
|
||||
* @param {object} root The discussion list root element
|
||||
*/
|
||||
var registerEventListeners = function(root) {
|
||||
root.on('click', Selectors.favourite.toggle, function(e) {
|
||||
var toggleElement = $(this);
|
||||
var forumId = toggleElement.data('forumid');
|
||||
var discussionId = toggleElement.data('discussionid');
|
||||
var subscriptionState = toggleElement.data('targetstate');
|
||||
|
||||
Repository.setFavouriteDiscussionState(forumId, discussionId, subscriptionState)
|
||||
.then(function(context) {
|
||||
return Templates.render('mod_forum/discussion_favourite_toggle', context);
|
||||
})
|
||||
.then(function(html, js) {
|
||||
return Templates.replaceNode(toggleElement, html, js);
|
||||
})
|
||||
.then(function() {
|
||||
return String.get_string("favouriteupdated", "forum")
|
||||
.done(function(s) {
|
||||
return Notification.addNotification({
|
||||
message: s,
|
||||
type: "info"
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: function(root) {
|
||||
registerEventListeners(root);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* This module is the highest level module for the calendar. It is
|
||||
* responsible for initialising all of the components required for
|
||||
* the calendar to run. It also coordinates the interaction between
|
||||
* components by listening for and responding to different events
|
||||
* triggered within the calendar UI.
|
||||
*
|
||||
* @module mod_forum/pin_toggle
|
||||
* @package mod_forum
|
||||
* @copyright 2018 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/ajax',
|
||||
'core/str',
|
||||
'core/templates',
|
||||
'core/notification',
|
||||
'mod_forum/repository',
|
||||
'mod_forum/selectors',
|
||||
'core/str',
|
||||
], function(
|
||||
$,
|
||||
Ajax,
|
||||
Str,
|
||||
Templates,
|
||||
Notification,
|
||||
Repository,
|
||||
Selectors,
|
||||
String
|
||||
) {
|
||||
|
||||
/**
|
||||
* Registery event listeners for the pin toggle.
|
||||
*
|
||||
* @param {object} root The calendar root element
|
||||
*/
|
||||
var registerEventListeners = function(root) {
|
||||
root.on('click', Selectors.pin.toggle, function(e) {
|
||||
var toggleElement = $(this);
|
||||
var forumid = toggleElement.data('forumid');
|
||||
var discussionid = toggleElement.data('discussionid');
|
||||
var pinstate = toggleElement.data('targetstate');
|
||||
Repository.setPinDiscussionState(forumid, discussionid, pinstate)
|
||||
.then(function(context) {
|
||||
return Templates.render('mod_forum/discussion_pin_toggle', context);
|
||||
})
|
||||
.then(function(html, js) {
|
||||
return Templates.replaceNode(toggleElement, html, js);
|
||||
})
|
||||
.then(function() {
|
||||
return String.get_string("pinupdated", "forum")
|
||||
.done(function(s) {
|
||||
return Notification.addNotification({
|
||||
message: s,
|
||||
type: "info"
|
||||
});
|
||||
});
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: function(root) {
|
||||
registerEventListeners(root);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -52,7 +52,25 @@ define(['core/ajax'], function(Ajax) {
|
||||
subject: subject
|
||||
}
|
||||
};
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the favourite state for a discussion in a forum.
|
||||
*
|
||||
* @param {number} forumId ID of the forum the discussion belongs to
|
||||
* @param {number} discussionId ID of the discussion with the subscription state
|
||||
* @param {null|date} targetState Set the favourite state. True == favourited; false == unfavourited.
|
||||
* @return {object} jQuery promise
|
||||
*/
|
||||
var setFavouriteDiscussionState = function(forumId, discussionId, targetState) {
|
||||
var request = {
|
||||
methodname: 'mod_forum_toggle_favourite_state',
|
||||
args: {
|
||||
discussionid: discussionId,
|
||||
targetstate: targetState
|
||||
}
|
||||
};
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
|
||||
@@ -62,7 +80,25 @@ define(['core/ajax'], function(Ajax) {
|
||||
args: {
|
||||
forumid: forumId,
|
||||
discussionid: discussionId,
|
||||
targetstate: targetState
|
||||
targetstate: targetState}
|
||||
};
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the pinned state for the discussion provided.
|
||||
*
|
||||
* @param {number} forumid
|
||||
* @param {number} discussionid
|
||||
* @param {boolean} targetstate
|
||||
* @return {*|Promise}
|
||||
*/
|
||||
var setPinDiscussionState = function(forumid, discussionid, targetstate) {
|
||||
var request = {
|
||||
methodname: 'mod_forum_set_pin_state',
|
||||
args: {
|
||||
discussionid: discussionid,
|
||||
targetstate: targetstate
|
||||
}
|
||||
};
|
||||
return Ajax.call([request])[0];
|
||||
@@ -71,6 +107,8 @@ define(['core/ajax'], function(Ajax) {
|
||||
return {
|
||||
setDiscussionSubscriptionState: setDiscussionSubscriptionState,
|
||||
addDiscussionPost: addDiscussionPost,
|
||||
setDiscussionLockState: setDiscussionLockState
|
||||
setDiscussionLockState: setDiscussionLockState,
|
||||
setFavouriteDiscussionState: setFavouriteDiscussionState,
|
||||
setPinDiscussionState: setPinDiscussionState
|
||||
};
|
||||
});
|
||||
|
||||
@@ -26,9 +26,6 @@ define([], function() {
|
||||
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"]',
|
||||
@@ -45,6 +42,12 @@ define([], function() {
|
||||
},
|
||||
lock: {
|
||||
toggle: "[data-action='toggle'][data-type='lock-toggle']",
|
||||
}
|
||||
},
|
||||
favourite: {
|
||||
toggle: "[data-type='favorite-toggle'][data-action='toggle']",
|
||||
},
|
||||
pin: {
|
||||
toggle: "[data-type='pin-toggle'][data-action='toggle']",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Exported discussion builder class.
|
||||
*
|
||||
* @package mod_forum
|
||||
* @copyright 2019 Peter Dias<[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_forum\local\builders;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_forum\local\entities\discussion as discussion_entity;
|
||||
use mod_forum\local\entities\forum as forum_entity;
|
||||
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
|
||||
use mod_forum\local\factories\exporter as exporter_factory;
|
||||
use mod_forum\local\factories\vault as vault_factory;
|
||||
use rating_manager;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Exported discussion builder class
|
||||
*
|
||||
* This class is an implementation of the builder pattern (loosely). It is responsible
|
||||
* for taking a set of related forums, discussions, and posts and generate the exported
|
||||
* version of the discussion.
|
||||
*
|
||||
* It encapsulates the complexity involved with exporting discussions. All of the relevant
|
||||
* additional resources will be loaded by this class in order to ensure the exporting
|
||||
* process can happen.
|
||||
*
|
||||
* See this doc for more information on the builder pattern:
|
||||
* https://designpatternsphp.readthedocs.io/en/latest/Creational/Builder/README.html
|
||||
*
|
||||
* @copyright 2019 Peter Dias<[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class exported_discussion {
|
||||
/** @var renderer_base $renderer Core renderer */
|
||||
private $renderer;
|
||||
|
||||
/** @var legacy_data_mapper_factory $legacydatamapperfactory Data mapper factory */
|
||||
private $legacydatamapperfactory;
|
||||
|
||||
/** @var exporter_factory $exporterfactory Exporter factory */
|
||||
private $exporterfactory;
|
||||
|
||||
/** @var vault_factory $vaultfactory Vault factory */
|
||||
private $vaultfactory;
|
||||
|
||||
/** @var rating_manager $ratingmanager Rating manager */
|
||||
private $ratingmanager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param renderer_base $renderer Core renderer
|
||||
* @param legacy_data_mapper_factory $legacydatamapperfactory Legacy data mapper factory
|
||||
* @param exporter_factory $exporterfactory Exporter factory
|
||||
* @param vault_factory $vaultfactory Vault factory
|
||||
* @param rating_manager $ratingmanager Rating manager
|
||||
*/
|
||||
public function __construct(
|
||||
renderer_base $renderer,
|
||||
legacy_data_mapper_factory $legacydatamapperfactory,
|
||||
exporter_factory $exporterfactory,
|
||||
vault_factory $vaultfactory,
|
||||
rating_manager $ratingmanager
|
||||
) {
|
||||
$this->renderer = $renderer;
|
||||
$this->legacydatamapperfactory = $legacydatamapperfactory;
|
||||
$this->exporterfactory = $exporterfactory;
|
||||
$this->vaultfactory = $vaultfactory;
|
||||
$this->ratingmanager = $ratingmanager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build any additional variables for the exported discussion for a given set of discussions.
|
||||
*
|
||||
* This will typically be used for a list of discussions in the same forum.
|
||||
*
|
||||
* @param stdClass $user The user to export the posts for.
|
||||
* @param forum_entity $forum The forum that each of the $discussions belong to
|
||||
* @param discussion_entity $discussion A list of all discussions that each of the $posts belong to
|
||||
* @return stdClass[] List of exported posts in the same order as the $posts array.
|
||||
*/
|
||||
public function build(
|
||||
stdClass $user,
|
||||
forum_entity $forum,
|
||||
discussion_entity $discussion
|
||||
) : array {
|
||||
|
||||
$favouriteids = [];
|
||||
if ($this->is_favourited($discussion, $forum->get_context(), $user)) {
|
||||
$favouriteids[] = $discussion->get_id();
|
||||
}
|
||||
|
||||
$groupsbyid = $this->get_groups_available_in_forum($forum);
|
||||
$discussionexporter = $this->exporterfactory->get_discussion_exporter(
|
||||
$user, $forum, $discussion, $groupsbyid, $favouriteids
|
||||
);
|
||||
|
||||
return (array) $discussionexporter->export($this->renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the groups details for all groups available to the forum.
|
||||
* @param forum_entity $forum The forum entity
|
||||
* @return stdClass[]
|
||||
*/
|
||||
private function get_groups_available_in_forum($forum) : array {
|
||||
$course = $forum->get_course_record();
|
||||
$coursemodule = $forum->get_course_module_record();
|
||||
|
||||
return groups_get_all_groups($course->id, 0, $coursemodule->groupingid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the provided discussion has been favourited by the user.
|
||||
*
|
||||
* @param discussion_entity $discussion The discussion record
|
||||
* @param \context_module $forumcontext Forum context
|
||||
* @param \stdClass $user The user to check the favourite against
|
||||
*
|
||||
* @return bool Whether or not the user has favourited the discussion
|
||||
*/
|
||||
public function is_favourited(discussion_entity $discussion, \context_module $forumcontext, \stdClass $user) {
|
||||
if (!isloggedin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
|
||||
return $ufservice->favourite_exists('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class exported_discussion_summaries {
|
||||
$latestposts = $postvault->get_latest_post_id_for_discussion_ids($user, $discussionids, $canseeanyprivatereply);
|
||||
|
||||
$unreadcounts = [];
|
||||
|
||||
$favourites = $this->get_favourites($user);
|
||||
$forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
|
||||
$forumrecord = $forumdatamapper->to_legacy_object($forum);
|
||||
|
||||
@@ -143,12 +143,34 @@ class exported_discussion_summaries {
|
||||
$groupsbyauthorid,
|
||||
$replycounts,
|
||||
$unreadcounts,
|
||||
$latestposts
|
||||
$latestposts,
|
||||
$favourites
|
||||
);
|
||||
|
||||
return (array) $summaryexporter->export($this->renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all favourited discussions.
|
||||
*
|
||||
* @param stdClass $user The user we are getting favourites for
|
||||
* @return int[] A list of favourited itemids
|
||||
*/
|
||||
private function get_favourites(stdClass $user) : array {
|
||||
$ids = [];
|
||||
|
||||
if (isloggedin()) {
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
|
||||
$favourites = $ufservice->find_favourites_by_type('mod_forum', 'discussions');
|
||||
foreach ($favourites as $favourite) {
|
||||
$ids[] = $favourite->itemid;
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the groups details for all groups available to the forum.
|
||||
* @param forum_entity $forum The forum entity
|
||||
|
||||
@@ -300,6 +300,18 @@ class discussion {
|
||||
return $this->get_group_id() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pinned value for this entity
|
||||
*
|
||||
* @param int $targetstate The state to change the pin to
|
||||
* @return bool
|
||||
*/
|
||||
public function set_pinned(int $targetstate): void {
|
||||
if ($targetstate != $this->pinned) {
|
||||
$this->pinned = $targetstate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the discussion is timed.
|
||||
*
|
||||
|
||||
@@ -96,6 +96,7 @@ class discussion extends exporter {
|
||||
'userstate' => [
|
||||
'type' => [
|
||||
'subscribed' => ['type' => PARAM_BOOL],
|
||||
'favourited' => ['type' => PARAM_BOOL],
|
||||
],
|
||||
],
|
||||
'capabilities' => [
|
||||
@@ -105,6 +106,7 @@ class discussion extends exporter {
|
||||
'pin' => ['type' => PARAM_BOOL],
|
||||
'post' => ['type' => PARAM_BOOL],
|
||||
'manage' => ['type' => PARAM_BOOL],
|
||||
'favourite' => ['type' => PARAM_BOOL]
|
||||
]
|
||||
],
|
||||
'urls' => [
|
||||
@@ -119,7 +121,11 @@ class discussion extends exporter {
|
||||
'type' => PARAM_URL,
|
||||
],
|
||||
'markasread' => ['type' => PARAM_URL],
|
||||
'subscribe' => ['type' => PARAM_URL]
|
||||
'subscribe' => ['type' => PARAM_URL],
|
||||
'pin' => [
|
||||
'optional' => true,
|
||||
'type' => PARAM_URL,
|
||||
],
|
||||
],
|
||||
],
|
||||
'timed' => [
|
||||
@@ -151,6 +157,7 @@ class discussion extends exporter {
|
||||
|
||||
$capabilitymanager = $this->related['capabilitymanager'];
|
||||
$urlfactory = $this->related['urlfactory'];
|
||||
$favouriteids = isset($this->related['favouriteids']) ? $this->related['favouriteids'] : [];
|
||||
|
||||
$forum = $this->related['forum'];
|
||||
$forumrecord = $this->get_forum_record();
|
||||
@@ -195,14 +202,16 @@ class discussion extends exporter {
|
||||
'locked' => $discussion->get_locked()
|
||||
],
|
||||
'userstate' => [
|
||||
'subscribed' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord, $discussion->get_id())
|
||||
'subscribed' => \mod_forum\subscriptions::is_subscribed($user->id, $forumrecord, $discussion->get_id()),
|
||||
'favourited' => in_array($discussion->get_id(), $favouriteids) ? true : false,
|
||||
],
|
||||
'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),
|
||||
'manage' => $capabilitymanager->can_manage_forum($user)
|
||||
'manage' => $capabilitymanager->can_manage_forum($user),
|
||||
'favourite' => $capabilitymanager->can_favourite_discussion($user, $discussion) // Defaulting to true until we get capabilities sorted
|
||||
],
|
||||
'urls' => [
|
||||
'view' => $urlfactory->get_discussion_view_url_from_discussion($discussion)->out(false),
|
||||
@@ -219,6 +228,10 @@ class discussion extends exporter {
|
||||
)->out(false);
|
||||
}
|
||||
|
||||
if ($capabilitymanager->can_pin_discussions($user)) {
|
||||
$data['urls']['pin'] = $urlfactory->get_pin_discussion_url_from_discussion($discussion)->out(false);
|
||||
}
|
||||
|
||||
if ($groupdata) {
|
||||
$data['group'] = $groupdata;
|
||||
}
|
||||
@@ -255,7 +268,8 @@ class discussion extends exporter {
|
||||
'urlfactory' => 'mod_forum\local\factories\url',
|
||||
'user' => 'stdClass',
|
||||
'groupsbyid' => 'stdClass[]',
|
||||
'latestpostid' => 'int?'
|
||||
'latestpostid' => 'int?',
|
||||
'favouriteids' => 'int[]?'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ class discussion_summaries extends exporter {
|
||||
'capabilitymanager' => 'mod_forum\local\managers\capability',
|
||||
'urlfactory' => 'mod_forum\local\factories\url',
|
||||
'user' => 'stdClass',
|
||||
'favouriteids' => 'int[]?'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ class discussion_summary extends exporter {
|
||||
'capabilitymanager' => 'mod_forum\local\managers\capability',
|
||||
'urlfactory' => 'mod_forum\local\factories\url',
|
||||
'user' => 'stdClass',
|
||||
'favouriteids' => 'int[]?'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use mod_forum\local\builders\exported_posts as exported_posts_builder;
|
||||
use mod_forum\local\builders\exported_discussion_summaries as exported_discussion_summaries_builder;
|
||||
use mod_forum\local\builders\exported_discussion as exported_discussion_builder;
|
||||
use mod_forum\local\factories\vault as vault_factory;
|
||||
use mod_forum\local\factories\legacy_data_mapper as legacy_data_mapper_factory;
|
||||
use mod_forum\local\factories\exporter as exporter_factory;
|
||||
@@ -108,4 +109,19 @@ class builder {
|
||||
$this->managerfactory
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the exported discussion builder.
|
||||
*
|
||||
* @return exported_discussion_summaries_builder
|
||||
*/
|
||||
public function get_exported_discussion_builder() : exported_discussion_builder {
|
||||
return new exported_discussion_builder(
|
||||
$this->rendererbase,
|
||||
$this->legacydatamapperfactory,
|
||||
$this->exporterfactory,
|
||||
$this->vaultfactory,
|
||||
$this->managerfactory->get_rating_manager()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,8 @@ class exporter {
|
||||
stdClass $user,
|
||||
forum_entity $forum,
|
||||
discussion_entity $discussion,
|
||||
array $groupsbyid = []
|
||||
array $groupsbyid = [],
|
||||
array $favouriteids = []
|
||||
) : discussion_exporter {
|
||||
return new discussion_exporter($discussion, [
|
||||
'context' => $forum->get_context(),
|
||||
@@ -132,7 +133,8 @@ class exporter {
|
||||
'user' => $user,
|
||||
'legacydatamapperfactory' => $this->legacydatamapperfactory,
|
||||
'latestpostid' => null,
|
||||
'groupsbyid' => $groupsbyid
|
||||
'groupsbyid' => $groupsbyid,
|
||||
'favouriteids' => $favouriteids
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -166,7 +168,8 @@ class exporter {
|
||||
array $groupsbyauthorid = [],
|
||||
array $discussionreplycount = [],
|
||||
array $discussionunreadcount = [],
|
||||
array $latestpostid = []
|
||||
array $latestpostid = [],
|
||||
array $favourites = []
|
||||
) : discussion_summaries_exporter {
|
||||
return new discussion_summaries_exporter(
|
||||
$discussions,
|
||||
@@ -182,6 +185,7 @@ class exporter {
|
||||
'capabilitymanager' => $this->managerfactory->get_capability_manager($forum),
|
||||
'urlfactory' => $this->urlfactory,
|
||||
'user' => $user,
|
||||
'favouriteids' => $favourites
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,15 @@ class renderer {
|
||||
$ratingmanager,
|
||||
$this->entityfactory->get_exported_posts_sorter(),
|
||||
$baseurl,
|
||||
$notifications
|
||||
$notifications,
|
||||
function($discussion, $user, $forum) {
|
||||
$exportbuilder = $this->builderfactory->get_exported_discussion_builder();
|
||||
return $exportedposts = $exportbuilder->build(
|
||||
$user,
|
||||
$forum,
|
||||
$discussion
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -457,4 +457,19 @@ class url {
|
||||
'd' => $discussion->get_id()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the pinned discussion link
|
||||
*
|
||||
* @param discussion_entity $discussion
|
||||
* @return moodle_url
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public function get_pin_discussion_url_from_discussion(discussion_entity $discussion) : moodle_url {
|
||||
return new moodle_url('discuss.php', [
|
||||
'sesskey' => sesskey(),
|
||||
'd' => $discussion->get_id(),
|
||||
'pin' => $discussion->is_pinned() ? FORUM_DISCUSSION_UNPINNED : FORUM_DISCUSSION_PINNED
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,6 +321,18 @@ class capability {
|
||||
return forum_user_can_post($forumrecord, $discussionrecord, $user, $coursemodule, $course, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the user favourite the discussion
|
||||
*
|
||||
* @param stdClass $user The user to check
|
||||
* @param discussion_entity $discussion The discussion to check
|
||||
* @return bool
|
||||
*/
|
||||
public function can_favourite_discussion(stdClass $user, discussion_entity $discussion) : bool {
|
||||
$context = $this->get_context();
|
||||
return has_capability('mod/forum:cantogglefavourite', $context, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the user view the content of the post in this discussion?
|
||||
*
|
||||
|
||||
@@ -90,6 +90,8 @@ class discussion {
|
||||
private $notifications;
|
||||
/** @var sorter_entity $exportedpostsorter Sorter for the exported posts */
|
||||
private $exportedpostsorter;
|
||||
/** @var callable $postprocessfortemplate Function to process exported posts before template rendering */
|
||||
private $postprocessfortemplate;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -123,7 +125,8 @@ class discussion {
|
||||
rating_manager $ratingmanager,
|
||||
sorter_entity $exportedpostsorter,
|
||||
moodle_url $baseurl,
|
||||
array $notifications = []
|
||||
array $notifications = [],
|
||||
callable $postprocessfortemplate = null
|
||||
) {
|
||||
$this->forum = $forum;
|
||||
$this->discussion = $discussion;
|
||||
@@ -140,6 +143,7 @@ class discussion {
|
||||
$this->notifications = $notifications;
|
||||
|
||||
$this->exportedpostsorter = $exportedpostsorter;
|
||||
$this->postprocessfortemplate = $postprocessfortemplate;
|
||||
|
||||
$forumdatamapper = $this->legacydatamapperfactory->get_forum_data_mapper();
|
||||
$this->forumrecord = $forumdatamapper->to_legacy_object($forum);
|
||||
@@ -173,7 +177,12 @@ class discussion {
|
||||
|
||||
$posts = array_merge([$firstpost], array_values($replies));
|
||||
|
||||
$exporteddiscussion = $this->get_exported_discussion($user);
|
||||
if ($this->postprocessfortemplate !== null) {
|
||||
$exporteddiscussion = ($this->postprocessfortemplate) ($this->discussion, $user, $this->forum);
|
||||
} else {
|
||||
$exporteddiscussion = $this->get_exported_discussion($user);
|
||||
}
|
||||
|
||||
$exporteddiscussion = array_merge($exporteddiscussion, [
|
||||
'notifications' => $this->get_notifications($user),
|
||||
'html' => [
|
||||
@@ -197,10 +206,6 @@ class discussion {
|
||||
$exporteddiscussion['html']['movediscussion'] = $this->get_move_discussion_html();
|
||||
}
|
||||
|
||||
if ($capabilities['pin']) {
|
||||
$exporteddiscussion['html']['pindiscussion'] = $this->get_pin_discussion_html();
|
||||
}
|
||||
|
||||
return $this->renderer->render_from_template('mod_forum/forum_discussion', $exporteddiscussion);
|
||||
}
|
||||
|
||||
@@ -325,30 +330,6 @@ class discussion {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML to render the pin discussion button.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_pin_discussion_html() : string {
|
||||
$discussion = $this->discussion;
|
||||
|
||||
if ($discussion->is_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', ['pin' => $pinlink, 'd' => $discussion->get_id()]),
|
||||
$pintext,
|
||||
'post'
|
||||
);
|
||||
return $this->renderer->render($button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML to render the export discussion button.
|
||||
*
|
||||
|
||||
@@ -171,6 +171,10 @@ class discussion_list {
|
||||
),
|
||||
'hasmore' => ($alldiscussionscount > $pagesize),
|
||||
'notifications' => $this->get_notifications($user, $groupid),
|
||||
'settings' => [
|
||||
'excludetext' => true,
|
||||
'togglemoreicon' => true
|
||||
]
|
||||
];
|
||||
|
||||
if (!$discussions) {
|
||||
@@ -310,7 +314,8 @@ class discussion_list {
|
||||
$user->id,
|
||||
$sortorder,
|
||||
$this->get_page_size($pagesize),
|
||||
$this->get_page_number($pageno) * $this->get_page_size($pagesize));
|
||||
$this->get_page_number($pageno) * $this->get_page_size($pagesize),
|
||||
$user);
|
||||
} else {
|
||||
return $discussions = $discussionvault->get_from_forum_id_and_group_id(
|
||||
$forum->get_id(),
|
||||
@@ -319,7 +324,8 @@ class discussion_list {
|
||||
$user->id,
|
||||
$sortorder,
|
||||
$this->get_page_size($pagesize),
|
||||
$this->get_page_number($pageno) * $this->get_page_size($pagesize));
|
||||
$this->get_page_number($pageno) * $this->get_page_size($pagesize),
|
||||
$user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,10 @@ class author extends db_table_vault {
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param \stdClass|null $user The user object
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, \stdClass $user = null) : string {
|
||||
$selectsql = 'SELECT * FROM {' . self::TABLE . '} ' . $this->get_table_alias();
|
||||
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
|
||||
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
|
||||
|
||||
@@ -72,9 +72,11 @@ abstract class db_table_vault {
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param object|null $user The user object
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string;
|
||||
abstract protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null,
|
||||
\stdClass $user = null) : string;
|
||||
|
||||
/**
|
||||
* Convert the DB records into entities. The list of records will have been
|
||||
|
||||
@@ -58,9 +58,10 @@ class discussion extends db_table_vault {
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param \stdClass|null $user The user object
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, \stdClass $user = null) : string {
|
||||
$selectsql = 'SELECT * FROM {' . self::TABLE . '} ' . $this->get_table_alias();
|
||||
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
|
||||
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
|
||||
|
||||
@@ -74,17 +74,34 @@ class discussion_list extends db_table_vault {
|
||||
return 'd';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the favourite table alias
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_favourite_alias() : string {
|
||||
return 'favalias';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the SQL to be used in get_records_sql.
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param string|null $joinsql Additional join conditions for the sql
|
||||
* @param stdClass|null $user User we are performing this query for
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_get_records_sql(string $wheresql = null, ?string $sortsql = null) : string {
|
||||
protected function generate_get_records_sql(string $wheresql = null, ?string $sortsql = null, stdClass $user = null) : string {
|
||||
$alias = $this->get_table_alias();
|
||||
$db = $this->get_db();
|
||||
|
||||
list($favsql, $favparams) = $this->get_favourite_sql($user);
|
||||
foreach ($favparams as $key => $param) {
|
||||
$favsql = str_replace(":$key", "'$param'", $favsql);
|
||||
}
|
||||
|
||||
// Fetch:
|
||||
// - Discussion
|
||||
// - First post
|
||||
@@ -106,6 +123,7 @@ class discussion_list extends db_table_vault {
|
||||
$tables .= ' JOIN {user} fa ON fa.id = ' . $alias . '.userid';
|
||||
$tables .= ' JOIN {user} la ON la.id = ' . $alias . '.usermodified';
|
||||
$tables .= ' JOIN ' . $posttable->get_from_sql() . ' ON fp.id = ' . $alias . '.firstpost';
|
||||
$tables .= isset($favsql) ? $favsql : '';
|
||||
|
||||
$selectsql = 'SELECT ' . $fields . ' FROM ' . $tables;
|
||||
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
|
||||
@@ -178,7 +196,7 @@ class discussion_list extends db_table_vault {
|
||||
*
|
||||
* @param int|null $sortmethod
|
||||
*/
|
||||
public function get_sort_order(?int $sortmethod) : string {
|
||||
public function get_sort_order(?int $sortmethod, $includefavourites = true) : string {
|
||||
global $CFG;
|
||||
|
||||
$alias = $this->get_table_alias();
|
||||
@@ -187,7 +205,6 @@ class discussion_list extends db_table_vault {
|
||||
$keyfield = "fp.created";
|
||||
$direction = "DESC";
|
||||
} else {
|
||||
// TODO consider user favourites...
|
||||
$keyfield = "{$alias}.timemodified";
|
||||
$direction = "DESC";
|
||||
|
||||
@@ -200,7 +217,9 @@ class discussion_list extends db_table_vault {
|
||||
}
|
||||
}
|
||||
|
||||
return "{$alias}.pinned DESC, {$keyfield} {$direction}, {$alias}.id DESC";
|
||||
$favouritesort = ($includefavourites ? ", {$this->get_favourite_alias()}.id DESC" : "");
|
||||
|
||||
return "{$alias}.pinned DESC $favouritesort , {$keyfield} {$direction}";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +268,8 @@ class discussion_list extends db_table_vault {
|
||||
?int $includepostsforuser,
|
||||
?int $sortorder,
|
||||
int $limit,
|
||||
int $offset
|
||||
int $offset,
|
||||
stdClass $user
|
||||
) {
|
||||
$alias = $this->get_table_alias();
|
||||
$wheresql = "{$alias}.forum = :forumid";
|
||||
@@ -263,7 +283,7 @@ class discussion_list extends db_table_vault {
|
||||
'forumid' => $forumid,
|
||||
]);
|
||||
|
||||
$sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder));
|
||||
$sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder, isloggedin()), $user);
|
||||
$records = $this->get_db()->get_records_sql($sql, $params, $offset, $limit);
|
||||
|
||||
return $this->transform_db_records_to_entities($records);
|
||||
@@ -289,7 +309,8 @@ class discussion_list extends db_table_vault {
|
||||
?int $includepostsforuser,
|
||||
?int $sortorder,
|
||||
int $limit,
|
||||
int $offset
|
||||
int $offset,
|
||||
stdClass $user
|
||||
) {
|
||||
$alias = $this->get_table_alias();
|
||||
|
||||
@@ -313,7 +334,7 @@ class discussion_list extends db_table_vault {
|
||||
'allgroupsid' => -1,
|
||||
]);
|
||||
|
||||
$sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder));
|
||||
$sql = $this->generate_get_records_sql($wheresql, $this->get_sort_order($sortorder, isloggedin()), $user);
|
||||
$records = $this->get_db()->get_records_sql($sql, $params, $offset, $limit);
|
||||
|
||||
return $this->transform_db_records_to_entities($records);
|
||||
@@ -390,4 +411,25 @@ class discussion_list extends db_table_vault {
|
||||
|
||||
return $this->get_db()->count_records_sql($this->generate_count_records_sql($wheresql), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the standard favouriting sql.
|
||||
*
|
||||
* @param stdClass $user The user we are getting the sql for
|
||||
* @return [$sql, $params] An array comprising of the sql and any associated params
|
||||
*/
|
||||
private function get_favourite_sql(?stdClass $user): array {
|
||||
$favsql = "";
|
||||
$favparams = [];
|
||||
|
||||
if ($user && isloggedin()) {
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$alias = $this->get_table_alias();
|
||||
$ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
|
||||
list($favsql, $favparams) = $ufservice->get_join_sql_by_type('mod_forum', 'discussions',
|
||||
$this->get_favourite_alias(), "$alias.id");
|
||||
}
|
||||
|
||||
return [$favsql, $favparams];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,10 @@ class forum extends db_table_vault {
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param \stdClass|null $user The user object
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, \stdClass $user = null) : string {
|
||||
$db = $this->get_db();
|
||||
$alias = $this->get_table_alias();
|
||||
|
||||
|
||||
@@ -63,9 +63,10 @@ class post extends db_table_vault {
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param \stdClass|null $user The user object
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, stdClass $user = null) : string {
|
||||
$table = self::TABLE;
|
||||
$alias = $this->get_table_alias();
|
||||
$fields = $alias . '.*';
|
||||
|
||||
@@ -57,9 +57,10 @@ class post_read_receipt_collection extends db_table_vault {
|
||||
*
|
||||
* @param string|null $wheresql Where conditions for the SQL
|
||||
* @param string|null $sortsql Order by conditions for the SQL
|
||||
* @param \stdClass|null $user The user object
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null) : string {
|
||||
protected function generate_get_records_sql(string $wheresql = null, string $sortsql = null, \stdClass $user = null) : string {
|
||||
$selectsql = 'SELECT * FROM {' . self::TABLE . '} ' . $this->get_table_alias();
|
||||
$selectsql .= $wheresql ? ' WHERE ' . $wheresql : '';
|
||||
$selectsql .= $sortsql ? ' ORDER BY ' . $sortsql : '';
|
||||
|
||||
@@ -405,5 +405,12 @@ $capabilities = array(
|
||||
),
|
||||
'clonepermissionsfrom' => 'mod/forum:canoverridediscussionlock'
|
||||
),
|
||||
'mod/forum:cantogglefavourite' => array(
|
||||
'captype' => 'write',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'archetypes' => array(
|
||||
'user' => CAP_ALLOW
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -145,4 +145,23 @@ $functions = array(
|
||||
'capabilities' => 'moodle/course:manageactivities',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
|
||||
),
|
||||
|
||||
'mod_forum_toggle_favourite_state' => array(
|
||||
'classname' => 'mod_forum_external',
|
||||
'methodname' => 'toggle_favourite_state',
|
||||
'classpath' => 'mod/forum/externallib.php',
|
||||
'description' => 'Toggle the favourite state',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
|
||||
),
|
||||
'mod_forum_set_pin_state' => array(
|
||||
'classname' => 'mod_forum_external',
|
||||
'methodname' => 'set_pin_state',
|
||||
'classpath' => 'mod/forum/externallib.php',
|
||||
'description' => 'Set the pin state',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
|
||||
),
|
||||
);
|
||||
|
||||
+151
-7
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die;
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
use mod_forum\local\exporters\post as post_exporter;
|
||||
use mod_forum\local\exporters\discussion as discussion_exporter;
|
||||
|
||||
class mod_forum_external extends external_api {
|
||||
|
||||
@@ -591,6 +592,11 @@ class mod_forum_external extends external_api {
|
||||
$canlock = has_capability('moodle/course:manageactivities', $modcontext, $USER);
|
||||
$replies = forum_count_discussion_replies($forumid, $sort, -1, $page, $perpage, $canseeprivatereplies);
|
||||
|
||||
if (isloggedin()) {
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
$ufservice = core_favourites\service_factory::get_service_for_user_context($usercontext);
|
||||
}
|
||||
$canfavourite = has_capability('mod/forum:cantogglefavourite', $modcontext, $USER);
|
||||
foreach ($alldiscussions as $discussion) {
|
||||
|
||||
// This function checks for qanda forums.
|
||||
@@ -639,7 +645,10 @@ class mod_forum_external extends external_api {
|
||||
|
||||
$discussion->locked = forum_discussion_is_locked($forum, $discussion);
|
||||
$discussion->canlock = $canlock;
|
||||
$discussion->starred = !empty($ufservice) ? $ufservice->favourite_exists('mod_forum', 'discussions',
|
||||
$discussionrec->id, $modcontext) : false;
|
||||
$discussion->canreply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
|
||||
$discussion->canfavourite = $canfavourite;
|
||||
|
||||
if (forum_is_author_hidden($discussion, $forum)) {
|
||||
$discussion->userid = null;
|
||||
@@ -729,8 +738,10 @@ class mod_forum_external extends external_api {
|
||||
'numunread' => new external_value(PARAM_INT, 'The number of unread discussions.'),
|
||||
'pinned' => new external_value(PARAM_BOOL, 'Is the discussion pinned'),
|
||||
'locked' => new external_value(PARAM_BOOL, 'Is the discussion locked'),
|
||||
'starred' => new external_value(PARAM_BOOL, 'Is the discussion starred'),
|
||||
'canreply' => new external_value(PARAM_BOOL, 'Can the user reply to the discussion'),
|
||||
'canlock' => new external_value(PARAM_BOOL, 'Can the user lock the discussion'),
|
||||
'canfavourite' => new external_value(PARAM_BOOL, 'Can the user star the discussion'),
|
||||
), 'post'
|
||||
)
|
||||
),
|
||||
@@ -1096,6 +1107,78 @@ class mod_forum_external extends external_api {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the favouriting value for the discussion provided
|
||||
*
|
||||
* @param int $discussionid The discussion we need to favourite
|
||||
* @param bool $targetstate The state of the favourite value
|
||||
* @return array The exported discussion
|
||||
*/
|
||||
public static function toggle_favourite_state($discussionid, $targetstate) {
|
||||
global $DB, $PAGE, $USER;
|
||||
|
||||
$params = self::validate_parameters(self::toggle_favourite_state_parameters(), [
|
||||
'discussionid' => $discussionid,
|
||||
'targetstate' => $targetstate
|
||||
]);
|
||||
|
||||
$vaultfactory = mod_forum\local\container::get_vault_factory();
|
||||
// Get the discussion vault and the corresponding discussion entity.
|
||||
$discussionvault = $vaultfactory->get_discussion_vault();
|
||||
$discussion = $discussionvault->get_from_id($params['discussionid']);
|
||||
|
||||
$forumvault = $vaultfactory->get_forum_vault();
|
||||
$forum = $forumvault->get_from_id($discussion->get_forum_id());
|
||||
$forumcontext = $forum->get_context();
|
||||
self::validate_context($forumcontext);
|
||||
|
||||
$managerfactory = mod_forum\local\container::get_manager_factory();
|
||||
$capabilitymanager = $managerfactory->get_capability_manager($forum);
|
||||
|
||||
// Does the user have the ability to favourite the discussion?
|
||||
if (!$capabilitymanager->can_favourite_discussion($USER, $discussion)) {
|
||||
throw new moodle_exception('cannotfavourite', 'forum');
|
||||
}
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
$ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
|
||||
$isfavourited = $ufservice->favourite_exists('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
|
||||
|
||||
$favouritefunction = $targetstate ? 'create_favourite' : 'delete_favourite';
|
||||
if ($isfavourited != (bool) $params['targetstate']) {
|
||||
$ufservice->{$favouritefunction}('mod_forum', 'discussions', $discussion->get_id(), $forumcontext);
|
||||
}
|
||||
|
||||
$exporterfactory = mod_forum\local\container::get_exporter_factory();
|
||||
$builder = mod_forum\local\container::get_builder_factory()->get_exported_discussion_builder();
|
||||
$favourited = ($builder->is_favourited($discussion, $forumcontext, $USER) ? [$discussion->get_id()] : []);
|
||||
$exporter = $exporterfactory->get_discussion_exporter($USER, $forum, $discussion, [], $favourited);
|
||||
return $exporter->export($PAGE->get_renderer('mod_forum'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return external_description
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function toggle_favourite_state_returns() {
|
||||
return discussion_exporter::get_read_structure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the parameters for the toggle_favourite_state method
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function toggle_favourite_state_parameters() {
|
||||
return new external_function_parameters(
|
||||
[
|
||||
'discussionid' => new external_value(PARAM_INT, 'The discussion to subscribe or unsubscribe'),
|
||||
'targetstate' => new external_value(PARAM_BOOL, 'The target state')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
@@ -1512,7 +1595,7 @@ class mod_forum_external extends external_api {
|
||||
* @return external_description
|
||||
*/
|
||||
public static function set_subscription_state_returns() {
|
||||
return \mod_forum\local\exporters\discussion::get_read_structure();
|
||||
return discussion_exporter::get_read_structure();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1554,7 +1637,6 @@ class mod_forum_external extends external_api {
|
||||
$discussion->toggle_locked_state($lockedvalue);
|
||||
$response = $discussionvault->update_discussion($discussion);
|
||||
$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'));
|
||||
@@ -1582,11 +1664,73 @@ class mod_forum_external extends external_api {
|
||||
*/
|
||||
public static function set_lock_state_returns() {
|
||||
return new external_single_structure([
|
||||
'id' => new external_value(PARAM_INT, 'The discussion we are locking.'),
|
||||
'locked' => new external_value(PARAM_BOOL, 'The locked state of the discussion.'),
|
||||
'times' => new external_single_structure([
|
||||
'locked' => new external_value(PARAM_INT, 'The locked time of the discussion.'),
|
||||
])
|
||||
'id' => new external_value(PARAM_INT, 'The discussion we are locking.'),
|
||||
'locked' => new external_value(PARAM_BOOL, 'The locked state of the discussion.'),
|
||||
'times' => new external_single_structure([
|
||||
'locked' => new external_value(PARAM_INT, 'The locked time of the discussion.'),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pin state.
|
||||
*
|
||||
* @param int $discussionid
|
||||
* @param bool $targetstate
|
||||
* @return \stdClass
|
||||
*/
|
||||
public static function set_pin_state($discussionid, $targetstate) {
|
||||
global $PAGE, $USER;
|
||||
$params = self::validate_parameters(self::set_pin_state_parameters(), [
|
||||
'discussionid' => $discussionid,
|
||||
'targetstate' => $targetstate,
|
||||
]);
|
||||
$vaultfactory = mod_forum\local\container::get_vault_factory();
|
||||
$managerfactory = mod_forum\local\container::get_manager_factory();
|
||||
$forumvault = $vaultfactory->get_forum_vault();
|
||||
$discussionvault = $vaultfactory->get_discussion_vault();
|
||||
$discussion = $discussionvault->get_from_id($params['discussionid']);
|
||||
$forum = $forumvault->get_from_id($discussion->get_forum_id());
|
||||
$capabilitymanager = $managerfactory->get_capability_manager($forum);
|
||||
|
||||
self::validate_context($forum->get_context());
|
||||
|
||||
$legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
|
||||
if (!$capabilitymanager->can_pin_discussions($USER)) {
|
||||
// Nothing to do. We won't actually output any content here though.
|
||||
throw new \moodle_exception('cannotpindiscussions', 'mod_forum');
|
||||
}
|
||||
|
||||
$discussion->set_pinned($targetstate);
|
||||
$discussionvault->update_discussion($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_pin_state_parameters() {
|
||||
return new external_function_parameters(
|
||||
[
|
||||
'discussionid' => new external_value(PARAM_INT, 'The discussion to pin or unpin', VALUE_REQUIRED,
|
||||
null, NULL_NOT_ALLOWED),
|
||||
'targetstate' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED,
|
||||
null, NULL_NOT_ALLOWED),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value.
|
||||
*
|
||||
* @return external_single_structure
|
||||
*/
|
||||
public static function set_pin_state_returns() {
|
||||
return discussion_exporter::get_read_structure();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ $string['activityoverview'] = 'There are new forum posts';
|
||||
$string['addanewdiscussion'] = 'Add a new discussion topic';
|
||||
$string['addanewquestion'] = 'Add a new question';
|
||||
$string['addanewtopic'] = 'Add a new topic';
|
||||
$string['addtofavourites'] = 'Star this discussion';
|
||||
$string['advancedsearch'] = 'Advanced search';
|
||||
$string['allforums'] = 'All forums';
|
||||
$string['allowdiscussions'] = 'Can a {$a} post to this forum?';
|
||||
@@ -74,11 +75,13 @@ $string['cannotmovenotvisible'] = 'Forum not visible';
|
||||
$string['cannotmovetonotexist'] = 'You can\'t move to that forum - it doesn\'t exist!';
|
||||
$string['cannotmovetonotfound'] = 'Target forum not found in this course.';
|
||||
$string['cannotmovetosingleforum'] = 'Cannot move discussion to a simple single discussion forum';
|
||||
$string['cannotpindiscussions'] = 'Sorry, you do not have permission to pin discussions.';
|
||||
$string['cannotpurgecachedrss'] = 'Could not purge the cached RSS feeds for the source and/or destination forum(s) - check your file permissionsforums';
|
||||
$string['cannotremovesubscriber'] = 'Could not remove subscriber with id {$a} from this forum!';
|
||||
$string['cannotreply'] = 'You cannot reply to this post';
|
||||
$string['cannotsplit'] = 'Discussions from this forum cannot be split';
|
||||
$string['cannotsubscribe'] = 'Sorry, but you must be a group member to subscribe.';
|
||||
$string['cannotfavourite'] = 'Sorry, but you do not have the permission to star discussions.';
|
||||
$string['cannottrack'] = 'Could not stop tracking that forum';
|
||||
$string['cannotunsubscribe'] = 'Could not unsubscribe you from that forum';
|
||||
$string['cannotupdatepost'] = 'You can not update this post';
|
||||
@@ -89,6 +92,8 @@ $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['clicktounfavourite'] = 'You have starred this discussion. Click to unstar.';
|
||||
$string['clicktofavourite'] = 'You have not starred this discussion. Click to star.';
|
||||
$string['completiondiscussions'] = 'Student must create discussions:';
|
||||
$string['completiondiscussionsdesc'] = 'Student must create at least {$a} discussion(s)';
|
||||
$string['completiondiscussionsgroup'] = 'Require discussions';
|
||||
@@ -233,6 +238,8 @@ $string['existingsubscribers'] = 'Existing subscribers';
|
||||
$string['exportdiscussion'] = 'Export whole discussion to portfolio';
|
||||
$string['exportattachmentname'] = 'Export attachment {$a} to portfolio';
|
||||
$string['firstpost'] = 'First post';
|
||||
$string['favourites'] = 'Starred';
|
||||
$string['favouriteupdated'] = 'Your star option has been updated.';
|
||||
$string['forcedreadtracking'] = 'Allow forced read tracking';
|
||||
$string['forcedreadtracking_desc'] = 'Allows forums to be set to forced read tracking. Will result in decreased performance for some users, particularly on courses with many forums and posts. When off, any forums previously set to Forced are treated as optional.';
|
||||
$string['forcesubscribed_help'] = 'This forum has been configured so that you cannot unsubscribe from discussions.';
|
||||
@@ -244,6 +251,7 @@ $string['forum:addquestion'] = 'Add question';
|
||||
$string['forum:allowforcesubscribe'] = 'Allow force subscribe';
|
||||
$string['forum:canoverridecutoff'] = 'Post to forums after their cut-off date';
|
||||
$string['forum:canoverridediscussionlock'] = 'Reply to locked discussions';
|
||||
$string['forum:cantogglefavourite'] = 'Can toggle favourites.';
|
||||
$string['forumauthorhidden'] = 'Author (hidden)';
|
||||
$string['forumblockingalmosttoomanyposts'] = 'You are approaching the posting threshold. You have posted {$a->numposts} times in the last {$a->blockperiod} and the limit is {$a->blockafter} posts.';
|
||||
$string['forumbodyhidden'] = 'This post cannot be viewed by you, probably because you have not posted in the discussion, the maximum editing time hasn\'t passed yet, the discussion has not started or the discussion has expired.';
|
||||
@@ -317,6 +325,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['lockupdated'] = 'The lock option has been updated.';
|
||||
$string['mailnow'] = 'Send forum post notifications with no editing-time delay';
|
||||
$string['manydiscussions'] = 'Discussions per page';
|
||||
$string['managesubscriptionsoff'] = 'Finish managing subscriptions';
|
||||
@@ -433,6 +442,8 @@ $string['permalink'] = 'Permalink';
|
||||
$string['permanentlinktopost'] = 'Permanent link to this post';
|
||||
$string['permanentlinktoparentpost'] = 'Permanent link to the parent of this post';
|
||||
$string['postisprivatereply'] = 'This post was made privately and is not visible to all users.';
|
||||
$string['pindiscussion'] = 'Pin this discussion';
|
||||
$string['pinupdated'] = 'The pin option has been updated.';
|
||||
$string['posttomygroups'] = 'Post a copy to all groups';
|
||||
$string['posttomygroups_help'] = 'Posts a copy of this message to all groups you have access to. Participants in groups you do not have access to will not see this post';
|
||||
$string['prevdiscussiona'] = 'Previous discussion: {$a}';
|
||||
@@ -527,6 +538,7 @@ $string['qandanotify'] = 'This is a question and answer forum. In order to see o
|
||||
$string['re'] = 'Re:';
|
||||
$string['readtherest'] = 'Read the rest of this topic';
|
||||
$string['removeallforumtags'] = 'Remove all forum tags';
|
||||
$string['removefromfavourites'] = 'Unstar this discussion';
|
||||
$string['replies'] = 'Replies';
|
||||
$string['repliesmany'] = '{$a} replies so far';
|
||||
$string['repliesone'] = '{$a} reply so far';
|
||||
@@ -568,6 +580,7 @@ $string['searchuserid'] = 'The Moodle ID of the author';
|
||||
$string['searchwhichforums'] = 'Choose which forums to search';
|
||||
$string['searchwords'] = 'These words can appear anywhere in the post';
|
||||
$string['seeallposts'] = 'See all posts made by this user';
|
||||
$string['settings'] = 'Settings';
|
||||
$string['shortpost'] = 'Short post';
|
||||
$string['showsubscribers'] = 'Show/edit current subscribers';
|
||||
$string['singleforum'] = 'A single simple discussion';
|
||||
@@ -638,6 +651,7 @@ $string['unsubscribeallempty'] = 'You are not subscribed to any forums. To disab
|
||||
$string['unsubscribed'] = 'Unsubscribed';
|
||||
$string['unsubscribeshort'] = 'Unsubscribe';
|
||||
$string['usermarksread'] = 'Manual message read marking';
|
||||
$string['unpindiscussion'] = 'Unpin this discussion';
|
||||
$string['viewalldiscussions'] = 'View all discussions';
|
||||
$string['viewthediscussion'] = 'View the discussion';
|
||||
$string['warnafter'] = 'Post threshold for warning';
|
||||
|
||||
@@ -6437,6 +6437,7 @@ function mod_forum_get_fontawesome_icon_map() {
|
||||
'mod_forum:t/selected' => 'fa-check',
|
||||
'mod_forum:t/subscribed' => 'fa-envelope-o',
|
||||
'mod_forum:t/unsubscribed' => 'fa-envelope-open-o',
|
||||
'mod_forum:t/star' => 'fa-star',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 390 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16pt" height="16pt" viewBox="0 0 16 16" version="1.1" preserveAspectRatio="xMinYMid meet">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,43.921569%,65.882353%);fill-opacity:1;" d="M 15.429688 5.777344 C 15.429688 5.90625 15.351562 6.050781 15.195312 6.207031 L 11.957031 9.367188 L 12.722656 13.832031 C 12.730469 13.871094 12.730469 13.929688 12.730469 14.007812 C 12.730469 14.132812 12.699219 14.238281 12.636719 14.324219 C 12.574219 14.414062 12.484375 14.457031 12.367188 14.457031 C 12.253906 14.457031 12.132812 14.417969 12.007812 14.347656 L 8 12.242188 L 3.992188 14.347656 C 3.859375 14.417969 3.742188 14.457031 3.632812 14.457031 C 3.507812 14.457031 3.414062 14.414062 3.351562 14.324219 C 3.289062 14.238281 3.257812 14.132812 3.257812 14.007812 C 3.257812 13.972656 3.265625 13.914062 3.277344 13.832031 L 4.042969 9.367188 L 0.792969 6.207031 C 0.644531 6.042969 0.570312 5.902344 0.570312 5.777344 C 0.570312 5.554688 0.738281 5.417969 1.070312 5.367188 L 5.554688 4.714844 L 7.5625 0.652344 C 7.675781 0.40625 7.820312 0.285156 8 0.285156 C 8.179688 0.285156 8.324219 0.40625 8.4375 0.652344 L 10.445312 4.714844 L 14.929688 5.367188 C 15.261719 5.417969 15.429688 5.554688 15.429688 5.777344 Z M 15.429688 5.777344 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -302,14 +302,17 @@ span.unread {
|
||||
}
|
||||
|
||||
.path-mod-forum .discussionsubscription,
|
||||
.path-mod-forum .discussionlock {
|
||||
.path-mod-forum .discussion-settings-menu,
|
||||
.path-mod-forum .discussionsubscription {
|
||||
margin-top: -10px;
|
||||
text-align: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.path-mod-forum .discussionsubscription > a > img,
|
||||
.path-mod-forum .discussionlock > a > img {
|
||||
.path-mod-forum .discussionlock > a > img,
|
||||
.path-mod-forum .favourite-discussion > a > img,
|
||||
.path-mod-forum .discussion-settings-menu > a > img {
|
||||
width: 12px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
{{!
|
||||
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_subscription_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.favourite}}
|
||||
<a
|
||||
class="btn btn-link"
|
||||
data-type="favorite-toggle"
|
||||
data-action="toggle"
|
||||
data-discussionid="{{id}}"
|
||||
data-forumid="{{forumid}}"
|
||||
tabindex="-1"
|
||||
{{#userstate.favourited}}
|
||||
data-targetstate="0"
|
||||
title="{{#str}}removefromfavourites, mod_forum{{/str}}"
|
||||
{{/userstate.favourited}}
|
||||
{{^userstate.favourited}}
|
||||
data-targetstate="1"
|
||||
title="{{#str}}addtofavourites, mod_forum{{/str}}"
|
||||
{{/userstate.favourited}}
|
||||
>
|
||||
{{$favouritecontent}}
|
||||
{{#userstate.favourited}}
|
||||
{{#str}}removefromfavourites, mod_forum{{/str}}
|
||||
{{/userstate.favourited}}
|
||||
{{^userstate.favourited}}
|
||||
{{#str}}addtofavourites, mod_forum{{/str}}
|
||||
{{/userstate.favourited}}
|
||||
{{/favouritecontent}}
|
||||
</a>
|
||||
{{/capabilities.favourite}}
|
||||
@@ -93,6 +93,11 @@
|
||||
{{#discussion.pinned}}
|
||||
{{#pix}}i/pinned, mod_forum, {{#str}}discussionpinned, mod_forum{{/str}}{{/pix}}
|
||||
{{/discussion.pinned}}
|
||||
{{^discussion.pinned}}
|
||||
{{#discussion}}
|
||||
{{> mod_forum/forum_discussion_favourite_toggle}}
|
||||
{{/discussion}}
|
||||
{{/discussion.pinned}}
|
||||
</td>
|
||||
<td scope="col" class="topic p-0 align-middle">
|
||||
<a class="p-3 p-l-0 w-100 h-100 d-block" href="{{discussion.urls.view}}">{{{discussion.name}}}</a>
|
||||
@@ -213,7 +218,14 @@
|
||||
</td>
|
||||
<td scope="col" class="p-0 align-middle">
|
||||
{{#discussion}}
|
||||
{{> mod_forum/discussion_subscription_toggle}}
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="p1-1">
|
||||
{{> mod_forum/discussion_subscription_toggle}}
|
||||
</div>
|
||||
<div class="p1-1 m-t-1" data-container='discussion-tools'>
|
||||
{{> mod_forum/forum_action_menu}}
|
||||
</div>
|
||||
</div>
|
||||
{{/discussion}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
}
|
||||
}}
|
||||
<a
|
||||
class="iconsmall"
|
||||
class="btn btn-link"
|
||||
data-type="lock-toggle"
|
||||
data-action="toggle"
|
||||
data-discussionid="{{id}}"
|
||||
data-forumid="{{forumid}}"
|
||||
data-state="{{locked}}"
|
||||
data-state="{{times.locked}}"
|
||||
href="#"
|
||||
{{#locked}}
|
||||
title="{{#str}}clicktounlockdiscussion, forum{{/str}}"
|
||||
@@ -50,9 +50,9 @@
|
||||
{{/locked}}
|
||||
>
|
||||
{{#locked}}
|
||||
{{#pix}}t/unlock, core, {{#str}}clicktounlockdiscussion, forum{{/str}}{{/pix}}{{#str}}locked, forum{{/str}}
|
||||
{{#str}}locked, forum{{/str}}
|
||||
{{/locked}}
|
||||
{{^locked}}
|
||||
{{#pix}}t/lock, core, {{#str}}clicktolockdiscussion, forum{{/str}}{{/pix}}{{#str}}notlocked, forum{{/str}}
|
||||
{{#str}}notlocked, forum{{/str}}
|
||||
{{/locked}}
|
||||
</a>
|
||||
@@ -0,0 +1,60 @@
|
||||
{{!
|
||||
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_subscription_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.pin}}
|
||||
<a
|
||||
class="btn btn-link"
|
||||
data-type="pin-toggle"
|
||||
data-action="toggle"
|
||||
data-discussionid="{{id}}"
|
||||
data-forumid="{{forumid}}"
|
||||
href="{{urls.pin}}"
|
||||
tabindex="-1"
|
||||
{{#pinned}}
|
||||
data-targetstate="0"
|
||||
title="{{#str}}unpindiscussion, mod_forum{{/str}}"
|
||||
{{/pinned}}
|
||||
{{^pinned}}
|
||||
data-targetstate="1"
|
||||
title="{{#str}}pindiscussion, mod_forum{{/str}}"
|
||||
{{/pinned}}
|
||||
>
|
||||
{{#pinned}}
|
||||
{{#str}}unpindiscussion, mod_forum{{/str}}
|
||||
{{/pinned}}
|
||||
{{^pinned}}
|
||||
{{#str}}pindiscussion, mod_forum{{/str}}
|
||||
{{/pinned}}
|
||||
</a>
|
||||
{{/capabilities.pin}}
|
||||
@@ -35,7 +35,7 @@
|
||||
{{#capabilities.subscribe}}
|
||||
<a
|
||||
href="{{{urls.subscribe}}}"
|
||||
class="p-3 btn btn-link"
|
||||
class="py-3 btn btn-link"
|
||||
data-type="subscription-toggle"
|
||||
data-action="toggle"
|
||||
data-discussionid="{{id}}"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
{{!
|
||||
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/forum-action-menu
|
||||
|
||||
This template renders action menu for each course.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
|
||||
}
|
||||
}}
|
||||
<div class="ml-auto dropdown">
|
||||
<button class="{{^settings.excludetext}}dropdown-toggle{{/settings.excludetext}} m-t-0 p-t-0 btn btn-link btn-icon"
|
||||
type="button"
|
||||
role="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
tabindex="0"
|
||||
aria-controls="forum-action-menu-{{id}}-menu"
|
||||
aria-expanded="false">
|
||||
{{#settings.togglemoreicon}}
|
||||
{{#pix}} i/moremenu, core{{/pix}}
|
||||
{{/settings.togglemoreicon}}
|
||||
{{^settings.togglemoreicon}}
|
||||
{{#pix}} i/settings, core{{/pix}}
|
||||
{{/settings.togglemoreicon}}
|
||||
{{^settings.excludetext}}
|
||||
{{#str}} settings, mod_forum {{/str}}
|
||||
{{/settings.excludetext}}
|
||||
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right"
|
||||
aria-labelledby="forum-action-menu-{{id}}-menu"
|
||||
data-rel="menu-content"
|
||||
role="menu"
|
||||
id="forum-action-menu-{{id}}-menu">
|
||||
{{#capabilities.favourite}}
|
||||
<div class="dropdown-item" role="menuitem">
|
||||
{{> mod_forum/discussion_favourite_toggle}}
|
||||
</div>
|
||||
{{/capabilities.favourite}}
|
||||
{{#capabilities.pin}}
|
||||
<div class="dropdown-item" role="menuitem">
|
||||
{{> mod_forum/discussion_pin_toggle}}
|
||||
</div>
|
||||
{{/capabilities.pin}}
|
||||
{{#capabilities.manage}}
|
||||
{{^istimelocked}}
|
||||
<div class="dropdown-item" role="menuitem">
|
||||
{{> forum/discussion_lock_toggle }}
|
||||
</div>
|
||||
{{/istimelocked}}
|
||||
{{/capabilities.manage}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,14 +32,13 @@
|
||||
|
||||
<div id="discussion-container-{{uniqid}}" data-content="forum-discussion">
|
||||
{{#html}}
|
||||
<div class="d-flex flex-wrap flex-row-reverse m-b-1" data-container="discussion-tools" style="text-align: right;">
|
||||
{{#capabilities.manage}}
|
||||
{{^istimelocked}}
|
||||
<div class="pl-1 discussionlock">
|
||||
{{> forum/discussion_lock_toggle }}
|
||||
<div class="d-flex flex-wrap flex-row-reverse m-b-1 text-right" data-container="discussion-tools">
|
||||
|
||||
<div class="pl-1">
|
||||
<div class="discussion-settings-menu">
|
||||
{{> mod_forum/forum_action_menu}}
|
||||
</div>
|
||||
{{/istimelocked}}
|
||||
{{/capabilities.manage}}
|
||||
</div>
|
||||
<div class="pl-1">{{{subscribe}}}</div>
|
||||
</div>
|
||||
{{{neighbourlinks}}}
|
||||
@@ -48,7 +47,6 @@
|
||||
<div>{{{exportdiscussion}}}</div>
|
||||
<div class="{{#exportdiscussion}}ml-2{{/exportdiscussion}}">{{{modeselectorform}}}</div>
|
||||
<div class="ml-2">{{{movediscussion}}}</div>
|
||||
<div class="ml-2">{{{pindiscussion}}}</div>
|
||||
</div>
|
||||
{{/html}}
|
||||
|
||||
@@ -61,11 +59,14 @@
|
||||
{{#html.neighbourlinks}}{{{.}}}{{/html.neighbourlinks}}
|
||||
</div>
|
||||
{{#js}}
|
||||
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 = $('[data-container="discussion-tools"]');
|
||||
LockToggle.init(root);
|
||||
require(['jquery', 'mod_forum/discussion', 'mod_forum/posts_list', 'mod_forum/lock_toggle', 'mod_forum/favourite_toggle', 'mod_forum/pin_toggle'],
|
||||
function($, Discussion, PostsList, LockToggle, FavouriteToggle, Pin) {
|
||||
var root = $("[data-content='forum-discussion']");
|
||||
Discussion.init(root);
|
||||
PostsList.init(root);
|
||||
root = $('[data-container="discussion-tools"]');
|
||||
LockToggle.init(root);
|
||||
FavouriteToggle.init(root);
|
||||
Pin.init(root);
|
||||
});
|
||||
{{/js}}
|
||||
{{/js}}
|
||||
@@ -0,0 +1,41 @@
|
||||
{{!
|
||||
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/forum_discussion_nested_post
|
||||
|
||||
Template which defines a forum post for sending in a single-post HTML email.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
}
|
||||
}}
|
||||
{{< mod_forum/discussion_favourite_toggle }}
|
||||
{{$favouritecontent}}
|
||||
{{#userstate.favourited}}
|
||||
{{#pix}}t/star, mod_forum, {{#str}}removefromfavourites, mod_forum{{/str}}{{/pix}}
|
||||
{{/userstate.favourited}}
|
||||
{{^userstate.favourited}}
|
||||
{{#pix}}t/emptystar, core, {{#str}}addtofavourites, mod_forum{{/str}}{{/pix}}
|
||||
{{/userstate.favourited}}
|
||||
{{/favouritecontent}}
|
||||
{{/ mod_forum/discussion_favourite_toggle }}
|
||||
@@ -127,6 +127,19 @@ class behat_mod_forum extends behat_base {
|
||||
$this->execute('behat_general::click_link', $this->escape($postsubject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens up the action menu for the discussion
|
||||
*
|
||||
* @Given /^I click on "(?P<post_subject_string>(?:[^"]|\\")*)" action menu$/
|
||||
* @param string $discussion The subject of the discussion
|
||||
*/
|
||||
public function i_click_on_action_menu($discussion) {
|
||||
$this->execute('behat_general::i_click_on_in_the', [
|
||||
"[data-container='discussion-tools'] [data-toggle='dropdown']", "css_element",
|
||||
"//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ') and contains(.,'$discussion')]",
|
||||
"xpath_element"
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the steps list to add a new discussion to a forum.
|
||||
|
||||
@@ -34,11 +34,14 @@ Feature: As a teacher, you can manually lock individual discussions when viewing
|
||||
Given I log in as "admin"
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to post "Discussion 1" in "Test forum name" forum
|
||||
And I press "Settings"
|
||||
Then "Lock" "link" should be visible
|
||||
And I follow "Lock"
|
||||
Then I should see "This discussion has been locked so you can no longer reply to it."
|
||||
And I press "Settings"
|
||||
Then "a[@title='Lock']" "css_element" should not be visible
|
||||
Then "Locked" "link" should be visible
|
||||
Then I should see "This discussion has been locked so you can no longer reply to it."
|
||||
And I press "Settings"
|
||||
And I follow "Discussion 2"
|
||||
Then I should not see "This discussion has been locked so you can no longer reply to it."
|
||||
And I log out
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
@mod @mod_forum @javascript
|
||||
Feature: A student can favourite a discussion via the forum settings menu
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "admin"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Forum" to section "1" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Description | Test forum description |
|
||||
And I add a new discussion to "Test forum name" forum with:
|
||||
| Subject | Discussion 1 |
|
||||
| Message | Discussion contents 1, first message |
|
||||
And I reply "Discussion 1" post from "Test forum name" forum with:
|
||||
| Subject | Reply 1 to discussion 1 |
|
||||
| Message | Discussion contents 1, second message |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
|
||||
Scenario: Student can favourite a discussion from within an individual discussion
|
||||
Given I reply "Discussion 1" post from "Test forum name" forum with:
|
||||
| Subject | Reply 2 to discussion 1 |
|
||||
| Message | Discussion contents 1, third message |
|
||||
And I wait until the page is ready
|
||||
And I open the action menu in "[data-container='discussion-tools']" "css_element"
|
||||
And I click on "[title='Star this discussion']" "css_element"
|
||||
And I wait "3" seconds
|
||||
And I open the action menu in "[data-container='discussion-tools']" "css_element"
|
||||
And I click on "[title='Unstar this discussion']" "css_element"
|
||||
|
||||
Scenario: Student can favourite a discussion from the discussion list
|
||||
Given I reply "Discussion 1" post from "Test forum name" forum with:
|
||||
| Subject | Reply 2 to discussion 1 |
|
||||
| Message | Discussion contents 1, third message |
|
||||
And I follow "Test forum name"
|
||||
And I click on "Discussion 1" action menu
|
||||
And I click on "[title='Star this discussion']" "css_element"
|
||||
And I click on "Discussion 1" action menu
|
||||
And I click on "[title='Unstar this discussion']" "css_element"
|
||||
@@ -180,6 +180,100 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertCount(0, $forums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the toggle favourite state
|
||||
*/
|
||||
public function test_mod_forum_toggle_favourite_state() {
|
||||
global $USER, $CFG, $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create a user.
|
||||
$user = self::getDataGenerator()->create_user(array('trackforums' => 1));
|
||||
|
||||
// Set to the user.
|
||||
self::setUser($user);
|
||||
|
||||
// Create courses to add the modules.
|
||||
$course1 = self::getDataGenerator()->create_course();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course1->id);
|
||||
|
||||
$record = new stdClass();
|
||||
$record->introformat = FORMAT_HTML;
|
||||
$record->course = $course1->id;
|
||||
$record->trackingtype = FORUM_TRACKING_OFF;
|
||||
$forum1 = self::getDataGenerator()->create_module('forum', $record);
|
||||
$forum1->introfiles = [];
|
||||
|
||||
// Add discussions to the forums.
|
||||
$record = new stdClass();
|
||||
$record->course = $course1->id;
|
||||
$record->userid = $user->id;
|
||||
$record->forum = $forum1->id;
|
||||
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
|
||||
|
||||
$response = mod_forum_external::toggle_favourite_state($discussion1->id, 1);
|
||||
$response = external_api::clean_returnvalue(mod_forum_external::toggle_favourite_state_returns(), $response);
|
||||
$this->assertTrue($response['userstate']['favourited']);
|
||||
|
||||
$response = mod_forum_external::toggle_favourite_state($discussion1->id, 0);
|
||||
$response = external_api::clean_returnvalue(mod_forum_external::toggle_favourite_state_returns(), $response);
|
||||
$this->assertFalse($response['userstate']['favourited']);
|
||||
|
||||
$this->setUser(0);
|
||||
try {
|
||||
$response = mod_forum_external::toggle_favourite_state($discussion1->id, 0);
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertEquals('requireloginerror', $e->errorcode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the toggle pin state
|
||||
*/
|
||||
public function test_mod_forum_set_pin_state() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create a user.
|
||||
$user = self::getDataGenerator()->create_user(array('trackforums' => 1));
|
||||
|
||||
// Set to the user.
|
||||
self::setUser($user);
|
||||
|
||||
// Create courses to add the modules.
|
||||
$course1 = self::getDataGenerator()->create_course();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course1->id);
|
||||
|
||||
$record = new stdClass();
|
||||
$record->introformat = FORMAT_HTML;
|
||||
$record->course = $course1->id;
|
||||
$record->trackingtype = FORUM_TRACKING_OFF;
|
||||
$forum1 = self::getDataGenerator()->create_module('forum', $record);
|
||||
$forum1->introfiles = [];
|
||||
|
||||
// Add discussions to the forums.
|
||||
$record = new stdClass();
|
||||
$record->course = $course1->id;
|
||||
$record->userid = $user->id;
|
||||
$record->forum = $forum1->id;
|
||||
$discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
|
||||
|
||||
try {
|
||||
$response = mod_forum_external::set_pin_state($discussion1->id, 1);
|
||||
} catch (Exception $e) {
|
||||
$this->assertEquals('cannotpindiscussions', $e->errorcode);
|
||||
}
|
||||
|
||||
self::setAdminUser();
|
||||
$response = mod_forum_external::set_pin_state($discussion1->id, 1);
|
||||
$response = external_api::clean_returnvalue(mod_forum_external::set_pin_state_returns(), $response);
|
||||
$this->assertTrue($response['pinned']);
|
||||
|
||||
$response = mod_forum_external::set_pin_state($discussion1->id, 0);
|
||||
$response = external_api::clean_returnvalue(mod_forum_external::set_pin_state_returns(), $response);
|
||||
$this->assertFalse($response['pinned']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get forum posts
|
||||
*/
|
||||
@@ -986,6 +1080,8 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||
'locked' => false,
|
||||
'canreply' => false,
|
||||
'canlock' => false,
|
||||
'starred' => false,
|
||||
'canfavourite' => true,
|
||||
);
|
||||
|
||||
// Call the external function passing forum id.
|
||||
@@ -1007,6 +1103,13 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||
|
||||
$this->assertEquals($expectedreturn, $discussions);
|
||||
|
||||
// Test the starring functionality return.
|
||||
$t = mod_forum_external::toggle_favourite_state($discussion1->id, 1);
|
||||
$expectedreturn['discussions'][0]['starred'] = true;
|
||||
$discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
|
||||
$discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
|
||||
$this->assertEquals($expectedreturn, $discussions);
|
||||
|
||||
// Call without required view discussion capability.
|
||||
$this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
|
||||
try {
|
||||
@@ -1667,7 +1770,7 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
|
||||
|
||||
// Check default values for capabilities.
|
||||
$enabledcaps = array('canviewdiscussion', 'canstartdiscussion', 'canreplypost', 'canviewrating', 'cancreateattachment',
|
||||
'canexportownpost', 'candeleteownpost', 'canallowforcesubscribe');
|
||||
'canexportownpost', 'cancantogglefavourite', 'candeleteownpost', 'canallowforcesubscribe');
|
||||
|
||||
unset($result['warnings']);
|
||||
foreach ($result as $capname => $capvalue) {
|
||||
|
||||
@@ -78,42 +78,43 @@ class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
|
||||
$datagenerator = $this->getDataGenerator();
|
||||
$vault = $this->vault;
|
||||
$user = $datagenerator->create_user();
|
||||
self::setUser($user);
|
||||
$course = $datagenerator->create_course();
|
||||
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
|
||||
|
||||
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, $user->id, null, 0, 0));
|
||||
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, $user->id, null, 0, 0, $user));
|
||||
|
||||
$now = time();
|
||||
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
|
||||
[$discussion2, $post2] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 9, 'timemodified' => 2]);
|
||||
[$hiddendiscussion, $post3] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now + 10, 'timemodified' => 3]);
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, false, null, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, false, null, null, 0, 0, $user));
|
||||
$this->assertCount(2, $summaries);
|
||||
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion1->id, $summaries[1]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 0, 0, $user));
|
||||
$this->assertCount(3, $summaries);
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, false, $user->id, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, false, $user->id, null, 0, 0, $user));
|
||||
$this->assertCount(3, $summaries);
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion1->id, $summaries[2]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 0, $user));
|
||||
$this->assertCount(1, $summaries);
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 1));
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 1, $user));
|
||||
$this->assertCount(1, $summaries);
|
||||
$this->assertEquals($discussion2->id, $summaries[0]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 2));
|
||||
$summaries = array_values($vault->get_from_forum_id($forum->id, true, null, null, 1, 2, $user));
|
||||
$this->assertCount(1, $summaries);
|
||||
$this->assertEquals($discussion1->id, $summaries[0]->get_discussion()->get_id());
|
||||
}
|
||||
@@ -127,10 +128,11 @@ class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
|
||||
$datagenerator = $this->getDataGenerator();
|
||||
$vault = $this->vault;
|
||||
$user = $datagenerator->create_user();
|
||||
self::setUser($user);
|
||||
$course = $datagenerator->create_course();
|
||||
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
|
||||
|
||||
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true, null, 0, 0));
|
||||
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true, null, 0, 0, $user));
|
||||
|
||||
$now = time();
|
||||
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
|
||||
@@ -152,7 +154,7 @@ class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
|
||||
['timestart' => $now + 11, 'timemodified' => 6, 'groupid' => 3]
|
||||
);
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 0, 0, $user));
|
||||
$this->assertCount(6, $summaries);
|
||||
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
|
||||
@@ -161,7 +163,7 @@ class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
|
||||
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, $user->id, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, $user->id, null, 0, 0, $user));
|
||||
$this->assertCount(6, $summaries);
|
||||
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[1]->get_discussion()->get_id());
|
||||
@@ -170,26 +172,26 @@ class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
|
||||
$this->assertEquals($discussion2->id, $summaries[4]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion1->id, $summaries[5]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 0, $user));
|
||||
$this->assertCount(1, $summaries);
|
||||
$this->assertEquals($hiddengroupdiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 1));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 1, $user));
|
||||
$this->assertCount(1, $summaries);
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 2));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], true, null, null, 1, 2, $user));
|
||||
$this->assertCount(1, $summaries);
|
||||
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, null, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [1, 2, 3], false, null, null, 0, 0, $user));
|
||||
$this->assertCount(4, $summaries);
|
||||
$this->assertEquals($groupdiscussion2->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($groupdiscussion1->id, $summaries[1]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion2->id, $summaries[2]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion1->id, $summaries[3]->get_discussion()->get_id());
|
||||
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [], true, null, null, 0, 0));
|
||||
$summaries = array_values($vault->get_from_forum_id_and_group_id($forum->id, [], true, null, null, 0, 0, $user));
|
||||
$this->assertCount(3, $summaries);
|
||||
$this->assertEquals($hiddendiscussion->id, $summaries[0]->get_discussion()->get_id());
|
||||
$this->assertEquals($discussion2->id, $summaries[1]->get_discussion()->get_id());
|
||||
@@ -231,10 +233,11 @@ class mod_forum_vaults_discussion_list_testcase extends advanced_testcase {
|
||||
$datagenerator = $this->getDataGenerator();
|
||||
$vault = $this->vault;
|
||||
$user = $datagenerator->create_user();
|
||||
self::setUser($user);
|
||||
$course = $datagenerator->create_course();
|
||||
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
|
||||
|
||||
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true, null, 0, 0));
|
||||
$this->assertEquals([], $vault->get_from_forum_id($forum->id, true, true, null, 0, 0, $user));
|
||||
|
||||
$now = time();
|
||||
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user, ['timestart' => $now - 10, 'timemodified' => 1]);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 357 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>Artboard 1</title><path d="M15.75,6.75l-3.49,3.4L13.09,15a1.29,1.29,0,0,1,0,.19c0,.26-.12.48-.39.48a.78.78,0,0,1-.38-.12L8,13.25,3.68,15.51a.81.81,0,0,1-.38.12c-.28,0-.4-.23-.4-.48a1.3,1.3,0,0,1,0-.19l.83-4.81L.24,6.75A.75.75,0,0,1,0,6.28c0-.29.3-.4.54-.44l4.83-.7L7.53.76C7.62.58,7.78.37,8,.37s.38.21.47.39l2.16,4.38,4.83.7c.23,0,.54.15.54.44A.72.72,0,0,1,15.75,6.75Zm-1.87.12-4.06-.6L8,2.59,6.18,6.26l-4.06.6L5.07,9.72l-.7,4L8,11.85l3.63,1.91-.69-4Z" fill="#999"/></svg>
|
||||
|
After Width: | Height: | Size: 539 B |
@@ -69,7 +69,6 @@ select {
|
||||
|
||||
thead .header th,
|
||||
tbody .discussion td {
|
||||
&.discussionlock,
|
||||
&.discussionsubscription {
|
||||
width: 16px;
|
||||
padding-left: 0.5em;
|
||||
@@ -84,14 +83,12 @@ select {
|
||||
}
|
||||
|
||||
.discussionsubscription,
|
||||
.discussionlock,
|
||||
.replies {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.topic,
|
||||
.discussionsubscription,
|
||||
.discussionlock,
|
||||
.topic.starter,
|
||||
.replies,
|
||||
.lastpost {
|
||||
|
||||
@@ -15035,8 +15035,7 @@ select {
|
||||
.path-mod-forum .forumheaderlist thead .header.lastpost {
|
||||
text-align: right; }
|
||||
|
||||
.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 thead .header th.discussionsubscription,
|
||||
.path-mod-forum .forumheaderlist tbody .discussion td.discussionsubscription {
|
||||
width: 16px;
|
||||
padding-left: 0.5em;
|
||||
@@ -15047,13 +15046,11 @@ 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 {
|
||||
|
||||
@@ -15292,8 +15292,7 @@ select {
|
||||
.path-mod-forum .forumheaderlist thead .header.lastpost {
|
||||
text-align: right; }
|
||||
|
||||
.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 thead .header th.discussionsubscription,
|
||||
.path-mod-forum .forumheaderlist tbody .discussion td.discussionsubscription {
|
||||
width: 16px;
|
||||
padding-left: 0.5em;
|
||||
@@ -15304,13 +15303,11 @@ 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 {
|
||||
|
||||
Reference in New Issue
Block a user