Merge branch 'MDL-66075-master' of git://github.com/lameze/moodle

This commit is contained in:
Jake Dallimore 2019-09-23 14:14:55 +08:00
commit 74e9aeca79
20 changed files with 959 additions and 40 deletions

View File

@ -584,6 +584,94 @@ class core_enrol_external extends external_api {
return new external_multiple_structure(core_user_external::user_description());
}
/**
* Returns description of method parameters
*
* @return external_function_parameters
*/
public static function search_users_parameters(): external_function_parameters {
return new external_function_parameters(
[
'courseid' => new external_value(PARAM_INT, 'course id'),
'search' => new external_value(PARAM_RAW, 'query'),
'searchanywhere' => new external_value(PARAM_BOOL, 'find a match anywhere, or only at the beginning'),
'page' => new external_value(PARAM_INT, 'Page number'),
'perpage' => new external_value(PARAM_INT, 'Number per page'),
]
);
}
/**
* Search course participants.
*
* @param int $courseid Course id
* @param string $search The query
* @param bool $searchanywhere Match anywhere in the string
* @param int $page Page number
* @param int $perpage Max per page
* @return array An array of users
* @throws moodle_exception
*/
public static function search_users(int $courseid, string $search, bool $searchanywhere, int $page, int $perpage): array {
global $PAGE, $DB, $CFG;
require_once($CFG->dirroot.'/enrol/locallib.php');
require_once($CFG->dirroot.'/user/lib.php');
$params = self::validate_parameters(
self::search_users_parameters(),
[
'courseid' => $courseid,
'search' => $search,
'searchanywhere' => $searchanywhere,
'page' => $page,
'perpage' => $perpage
]
);
$context = context_course::instance($params['courseid']);
try {
self::validate_context($context);
} catch (Exception $e) {
$exceptionparam = new stdClass();
$exceptionparam->message = $e->getMessage();
$exceptionparam->courseid = $params['courseid'];
throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
}
course_require_view_participants($context);
$course = get_course($params['courseid']);
$manager = new course_enrolment_manager($PAGE, $course);
$users = $manager->search_users($params['search'],
$params['searchanywhere'],
$params['page'],
$params['perpage']);
$results = [];
// Add also extra user fields.
$requiredfields = array_merge(
['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
get_extra_user_fields($context)
);
foreach ($users['users'] as $user) {
if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
$results[] = $userdetails;
}
}
return $results;
}
/**
* Returns description of method result value
*
* @return external_multiple_structure
*/
public static function search_users_returns(): external_multiple_structure {
global $CFG;
require_once($CFG->dirroot . '/user/externallib.php');
return new external_multiple_structure(core_user_external::user_description());
}
/**
* Returns description of method parameters
*

View File

@ -531,6 +531,35 @@ class course_enrolment_manager {
return $this->execute_search_queries($search, $fields, $countfields, $sql, $params, $page, $perpage, 0, $returnexactcount);
}
/**
* Searches through the enrolled users in this course.
*
* @param string $search The search term.
* @param bool $searchanywhere Can the search term be anywhere, or must it be at the start.
* @param int $page Starting at 0.
* @param int $perpage Number of users returned per page.
* @param bool $returnexactcount Return the exact total users using count_record or not.
* @return array with two or three elements:
* int totalusers Number users matching the search. (This element only exist if $returnexactcount was set to true)
* array users List of user objects returned by the query.
* boolean moreusers True if there are still more users, otherwise is False.
*/
public function search_users(string $search = '', bool $searchanywhere = false, int $page = 0, int $perpage = 25,
bool $returnexactcount = false) {
list($ufields, $params, $wherecondition) = $this->get_basic_search_conditions($search, $searchanywhere);
$fields = 'SELECT ' . $ufields;
$countfields = 'SELECT COUNT(u.id)';
$sql = " FROM {user} u
JOIN {user_enrolments} ue ON ue.userid = u.id
JOIN {enrol} e ON ue.enrolid = e.id
WHERE $wherecondition
AND e.courseid = :courseid";
$params['courseid'] = $this->course->id;
return $this->execute_search_queries($search, $fields, $countfields, $sql, $params, $page, $perpage, 0, $returnexactcount);
}
/**
* Gets an array containing some SQL to user for when selecting, params for
* that SQL, and the filter that was used in constructing the sql.

View File

@ -325,7 +325,7 @@ class core_course_enrolment_manager_testcase extends advanced_testcase {
}
/**
* Test case for test_get_potential_users and test_search_other_users tests.
* Test case for test_get_potential_users, test_search_other_users and test_search_users tests.
*
* @return array Dataset
*/
@ -337,4 +337,42 @@ class core_course_enrolment_manager_testcase extends advanced_testcase {
[5, true, 3, 3, false]
];
}
/**
* Test search_users function.
*
* @dataProvider search_users_provider
*
* @param int $perpage Number of users per page.
* @param bool $returnexactcount Return the exact count or not.
* @param int $expectedusers Expected number of users return.
* @param int $expectedtotalusers Expected total of users in database.
* @param bool $expectedmoreusers Expected for more users return or not.
*/
public function test_search_users($perpage, $returnexactcount, $expectedusers, $expectedtotalusers, $expectedmoreusers) {
global $PAGE;
$this->resetAfterTest();
$this->getDataGenerator()->create_and_enrol($this->course, 'student', ['firstname' => 'sutest 1']);
$this->getDataGenerator()->create_and_enrol($this->course, 'student', ['firstname' => 'sutest 2']);
$this->getDataGenerator()->create_and_enrol($this->course, 'student', ['firstname' => 'sutest 3']);
$manager = new course_enrolment_manager($PAGE, $this->course);
$users = $manager->search_users(
'sutest',
true,
0,
$perpage,
$returnexactcount
);
$this->assertCount($expectedusers, $users['users']);
$this->assertEquals($expectedmoreusers, $users['moreusers']);
if ($returnexactcount) {
$this->assertArrayHasKey('totalusers', $users);
$this->assertEquals($expectedtotalusers, $users['totalusers']);
} else {
$this->assertArrayNotHasKey('totalusers', $users);
}
}
}

View File

@ -1171,4 +1171,92 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$ue = $DB->count_records('user_enrolments', ['id' => $ueid]);
$this->assertEquals(0, $ue);
}
/**
* Test for core_enrol_external::test_search_users().
*/
public function test_search_users() {
global $DB;
$this->resetAfterTest(true);
$datagen = $this->getDataGenerator();
/** @var enrol_manual_plugin $manualplugin */
$manualplugin = enrol_get_plugin('manual');
$this->assertNotNull($manualplugin);
$studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
$teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
$course1 = $datagen->create_course();
$course2 = $datagen->create_course();
$user1 = $datagen->create_user(['firstname' => 'user 1']);
$user2 = $datagen->create_user(['firstname' => 'user 2']);
$user3 = $datagen->create_user(['firstname' => 'user 3']);
$teacher = $datagen->create_user(['firstname' => 'user 4']);
$instanceid = null;
$instances = enrol_get_instances($course1->id, true);
foreach ($instances as $inst) {
if ($inst->enrol == 'manual') {
$instanceid = (int)$inst->id;
break;
}
}
if (empty($instanceid)) {
$instanceid = $manualplugin->add_default_instance($course1);
if (empty($instanceid)) {
$instanceid = $manualplugin->add_instance($course1);
}
}
$this->assertNotNull($instanceid);
$instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
$manualplugin->enrol_user($instance, $user1->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
$manualplugin->enrol_user($instance, $user2->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
$manualplugin->enrol_user($instance, $user3->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
$manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
$this->setUser($teacher);
// Search for users in a course with enrolled users.
$result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30);
$this->assertCount(4, $result);
$this->expectException('moodle_exception');
// Search for users in a course without any enrolled users, shouldn't return anything.
$result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30);
$this->assertCount(0, $result);
// Search for invalid first name.
$result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30);
$this->assertCount(0, $result);
// Test pagination, it should return only 3 users.
$result = core_enrol_external::search_users($course1->id, 'user', true, 0, 3);
$this->assertCount(3, $result);
// Test pagination, it should return only 3 users.
$result = core_enrol_external::search_users($course1->id, 'user 1', true, 0, 1);
$result = $result[0];
$this->assertEquals($user1->id, $result['id']);
$this->assertEquals($user1->email, $result['email']);
$this->assertEquals(fullname($user1), $result['fullname']);
$this->setUser($user1);
// Search for users in a course with enrolled users.
$result = core_enrol_external::search_users($course1->id, 'user', true, 0, 30);
$this->assertCount(4, $result);
$this->expectException('moodle_exception');
// Search for users in a course without any enrolled users, shouldn't return anything.
$result = core_enrol_external::search_users($course2->id, 'user', true, 0, 30);
$this->assertCount(0, $result);
// Search for invalid first name.
$result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30);
$this->assertCount(0, $result);
}
}

View File

@ -634,6 +634,15 @@ $functions = array(
'type' => 'read',
'capabilities' => 'moodle/course:enrolreview'
),
'core_enrol_search_users' => [
'classname' => 'core_enrol_external',
'methodname' => 'search_users',
'classpath' => 'enrol/externallib.php',
'description' => 'Search within the list of course participants',
'ajax' => true,
'type' => 'read',
'capabilities' => 'moodle/course:viewparticipants',
],
'core_enrol_get_users_courses' => array(
'classname' => 'core_enrol_external',
'methodname' => 'get_users_courses',

View File

@ -0,0 +1,2 @@
define ("mod_forum/form-user-selector",["jquery","core/ajax","core/templates"],function(a,b,c){return{processResults:function processResults(b,c){var d=[];a.each(c,function(a,b){d.push({value:b.id,label:b._label})});return d},transport:function transport(d,e,f,g){var h,i=a(d).attr("courseid");h=b.call([{methodname:"core_enrol_search_users",args:{courseid:i,search:e,searchanywhere:!0,page:0,perpage:30}}]);h[0].then(function(b){var d=[],e=0;a.each(b,function(a,b){d.push(c.render("mod_forum/form-user-selector-suggestion",b))});return a.when.apply(a.when,d).then(function(){var c=arguments;a.each(b,function(a,b){b._label=c[e];e++});f(b)})}).fail(g)}}});
//# sourceMappingURL=form-user-selector.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../src/form-user-selector.js"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","promise","courseid","attr","call","methodname","args","search","searchanywhere","page","perpage","then","promises","i","render","when","apply","arguments","fail"],"mappings":"AAyBAA,OAAM,gCAAC,CAAC,QAAD,CAAW,WAAX,CAAwB,gBAAxB,CAAD,CAA4C,SAASC,CAAT,CAAYC,CAAZ,CAAkBC,CAAlB,CAA6B,CAC3E,MAAyD,CACrDC,cAAc,CAAE,wBAASC,CAAT,CAAmBC,CAAnB,CAA4B,CACxC,GAAIC,CAAAA,CAAK,CAAG,EAAZ,CACAN,CAAC,CAACO,IAAF,CAAOF,CAAP,CAAgB,SAASG,CAAT,CAAgBC,CAAhB,CAAsB,CAClCH,CAAK,CAACI,IAAN,CAAW,CACPC,KAAK,CAAEF,CAAI,CAACG,EADL,CAEPC,KAAK,CAAEJ,CAAI,CAACK,MAFL,CAAX,CAIH,CALD,EAMA,MAAOR,CAAAA,CACV,CAVoD,CAYrDS,SAAS,CAAE,mBAASX,CAAT,CAAmBY,CAAnB,CAA0BC,CAA1B,CAAmCC,CAAnC,CAA4C,IAC/CC,CAAAA,CAD+C,CAE/CC,CAAQ,CAAGpB,CAAC,CAACI,CAAD,CAAD,CAAYiB,IAAZ,CAAiB,UAAjB,CAFoC,CAInDF,CAAO,CAAGlB,CAAI,CAACqB,IAAL,CAAU,CAAC,CACjBC,UAAU,CAAE,yBADK,CAEjBC,IAAI,CAAE,CACFJ,QAAQ,CAAEA,CADR,CAEFK,MAAM,CAAET,CAFN,CAGFU,cAAc,GAHZ,CAIFC,IAAI,CAAE,CAJJ,CAKFC,OAAO,CAAE,EALP,CAFW,CAAD,CAAV,CAAV,CAWAT,CAAO,CAAC,CAAD,CAAP,CAAWU,IAAX,CAAgB,SAASxB,CAAT,CAAkB,CAC9B,GAAIyB,CAAAA,CAAQ,CAAG,EAAf,CACIC,CAAC,CAAG,CADR,CAIA/B,CAAC,CAACO,IAAF,CAAOF,CAAP,CAAgB,SAASG,CAAT,CAAgBC,CAAhB,CAAsB,CAClCqB,CAAQ,CAACpB,IAAT,CAAcR,CAAS,CAAC8B,MAAV,CAAiB,yCAAjB,CAA4DvB,CAA5D,CAAd,CACH,CAFD,EAKA,MAAOT,CAAAA,CAAC,CAACiC,IAAF,CAAOC,KAAP,CAAalC,CAAC,CAACiC,IAAf,CAAqBH,CAArB,EAA+BD,IAA/B,CAAoC,UAAW,CAClD,GAAIL,CAAAA,CAAI,CAAGW,SAAX,CACAnC,CAAC,CAACO,IAAF,CAAOF,CAAP,CAAgB,SAASG,CAAT,CAAgBC,CAAhB,CAAsB,CAClCA,CAAI,CAACK,MAAL,CAAcU,CAAI,CAACO,CAAD,CAAlB,CACAA,CAAC,EACJ,CAHD,EAIAd,CAAO,CAACZ,CAAD,CAEV,CARM,CAUV,CApBD,EAoBG+B,IApBH,CAoBQlB,CApBR,CAqBH,CAhDoD,CAoD5D,CArDK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Enrolled user selector module.\n *\n * @module mod_forum/form-user-selector\n * @class form-user-selector\n * @package mod_forum\n * @copyright 2019 Shamim Rezaie\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n return /** @alias module:mod_forum/form-user-selector */ {\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n var courseid = $(selector).attr('courseid');\n\n promise = Ajax.call([{\n methodname: 'core_enrol_search_users',\n args: {\n courseid: courseid,\n search: query,\n searchanywhere: true,\n page: 0,\n perpage: 30\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results, function(index, user) {\n promises.push(Templates.render('mod_forum/form-user-selector-suggestion', user));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n }).fail(failure);\n }\n\n };\n\n});\n"],"file":"form-user-selector.min.js"}

View File

@ -0,0 +1,79 @@
// 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/>.
/**
* Enrolled user selector module.
*
* @module mod_forum/form-user-selector
* @class form-user-selector
* @package mod_forum
* @copyright 2019 Shamim Rezaie
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
return /** @alias module:mod_forum/form-user-selector */ {
processResults: function(selector, results) {
var users = [];
$.each(results, function(index, user) {
users.push({
value: user.id,
label: user._label
});
});
return users;
},
transport: function(selector, query, success, failure) {
var promise;
var courseid = $(selector).attr('courseid');
promise = Ajax.call([{
methodname: 'core_enrol_search_users',
args: {
courseid: courseid,
search: query,
searchanywhere: true,
page: 0,
perpage: 30
}
}]);
promise[0].then(function(results) {
var promises = [],
i = 0;
// Render the label.
$.each(results, function(index, user) {
promises.push(Templates.render('mod_forum/form-user-selector-suggestion', user));
});
// Apply the label to the results.
return $.when.apply($.when, promises).then(function() {
var args = arguments;
$.each(results, function(index, user) {
user._label = args[i];
i++;
});
success(results);
return;
});
}).fail(failure);
}
};
});

View File

@ -0,0 +1,81 @@
<?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/>.
/**
* This file contains the form definition for discussion export.
*
* @package mod_forum
* @copyright 2019 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_forum\form;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
require_once($CFG->dirroot.'/mod/forum/lib.php');
require_once($CFG->libdir.'/formslib.php');
/**
* Export discussion form.
*
* @package mod_forum
* @copyright 2019 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL Juv3 or later
*/
class export_form extends \moodleform {
/**
* Define the form - called by parent constructor
*/
public function definition() {
$mform = $this->_form;
$forum = $this->_customdata['forum'];
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->setDefault('id', $forum->get_id());
$options = [
'ajax' => 'mod_forum/form-user-selector',
'multiple' => true,
'noselectionstring' => get_string('allusers', 'mod_forum'),
'courseid' => $forum->get_course_id(),
];
$mform->addElement('autocomplete', 'userids', get_string('users'), [], $options);
// Get the discussions on this forum.
$vaultfactory = \mod_forum\local\container::get_vault_factory();
$discussionvault = $vaultfactory->get_discussion_vault();
$discussions = array_map(function($discussion) {
return $discussion->get_name();
}, $discussionvault->get_all_discussions_in_forum($forum));
$options = [
'multiple' => true,
'noselectionstring' => get_string('alldiscussions', 'mod_forum'),
];
$mform->addElement('autocomplete', 'discussionids', get_string('discussions', 'mod_forum'), $discussions, $options);
// Export formats.
$formats = \core_plugin_manager::instance()->get_plugins_of_type('dataformat');
$options = [];
foreach ($formats as $format) {
$options[$format->name] = $format->displayname;
}
$mform->addElement('select', 'format', 'Format', $options);
$this->add_action_buttons(true, get_string('export', 'mod_forum'));
}
}

View File

@ -633,4 +633,14 @@ class capability {
return $canstart;
}
/**
* Checks whether the user can export the whole forum (discussions and posts).
*
* @param stdClass $user The user object.
* @return bool True if the user can export the forum or false otherwise.
*/
public function can_export_forum(stdClass $user) : bool {
return has_capability('mod/forum:exportforum', $this->get_context(), $user);
}
}

View File

@ -86,6 +86,20 @@ class discussion extends db_table_vault {
}, $results);
}
/**
* Get all discussions in the specified forum.
*
* @param forum_entity $forum
* @return array
*/
public function get_all_discussions_in_forum(forum_entity $forum): ?array {
$records = $this->get_db()->get_records(self::TABLE, [
'forum' => $forum->get_id(),
]);
return $this->transform_db_records_to_entities($records);
}
/**
* Get the first discussion in the specified forum.
*

View File

@ -112,33 +112,24 @@ class post extends db_table_vault {
bool $canseeprivatereplies,
string $orderby = 'created ASC'
) : array {
$alias = $this->get_table_alias();
[
'where' => $privatewhere,
'params' => $privateparams,
] = $this->get_private_reply_sql($user, $canseeprivatereplies);
$wheresql = "{$alias}.discussion = :discussionid {$privatewhere}";
$orderbysql = $alias . '.' . $orderby;
$sql = $this->generate_get_records_sql($wheresql, $orderbysql);
$records = $this->get_db()->get_records_sql($sql, array_merge([
'discussionid' => $discussionid,
], $privateparams));
return $this->transform_db_records_to_entities($records);
return $this->get_from_discussion_ids($user, [$discussionid], $canseeprivatereplies, $orderby);
}
/**
* Get the list of posts for the given discussions.
*
* @param stdClass $user The user to check the unread count for
* @param stdClass $user The user to load posts for.
* @param int[] $discussionids The list of discussion ids to load posts for
* @param bool $canseeprivatereplies Whether this user can see all private replies or not
* @param string $orderby Order the results
* @return post_entity[]
*/
public function get_from_discussion_ids(stdClass $user, array $discussionids, bool $canseeprivatereplies) : array {
public function get_from_discussion_ids(
stdClass $user,
array $discussionids,
bool $canseeprivatereplies,
string $orderby = ''
) : array {
if (empty($discussionids)) {
return [];
}
@ -153,12 +144,65 @@ class post extends db_table_vault {
$wheresql = "{$alias}.discussion {$insql} {$privatewhere}";
$sql = $this->generate_get_records_sql($wheresql, '');
if ($orderby) {
$orderbysql = $alias . '.' . $orderby;
} else {
$orderbysql = '';
}
$sql = $this->generate_get_records_sql($wheresql, $orderbysql);
$records = $this->get_db()->get_records_sql($sql, array_merge($params, $privateparams));
return $this->transform_db_records_to_entities($records);
}
/**
* The method returns posts made by the supplied users in the supplied discussions.
*
* @param stdClass $user Only used when restricting private replies
* @param int[] $discussionids The list of discussion ids to load posts for
* @param int[] $userids Only return posts made by these users
* @param bool $canseeprivatereplies Whether this user can see all private replies or not
* @param string $orderby Order the results
* @return post_entity[]
*/
public function get_from_discussion_ids_and_user_ids(
stdClass $user,
array $discussionids,
array $userids,
bool $canseeprivatereplies,
string $orderby = ''
): array {
if (empty($discussionids) || empty($userids)) {
return [];
}
$alias = $this->get_table_alias();
list($indiscussionssql, $indiscussionsparams) = $this->get_db()->get_in_or_equal($discussionids, SQL_PARAMS_NAMED);
list($inuserssql, $inusersparams) = $this->get_db()->get_in_or_equal($userids, SQL_PARAMS_NAMED);
[
'where' => $privatewhere,
'params' => $privateparams,
] = $this->get_private_reply_sql($user, $canseeprivatereplies);
$wheresql = "{$alias}.discussion {$indiscussionssql}
AND {$alias}.userid {$inuserssql}
{$privatewhere}";
if ($orderby) {
$orderbysql = $alias . '.' . $orderby;
} else {
$orderbysql = '';
}
$sql = $this->generate_get_records_sql($wheresql, $orderbysql);
$records = $this->get_db()->get_records_sql($sql, array_merge($indiscussionsparams, $inusersparams, $privateparams));
return $this->transform_db_records_to_entities($records);
}
/**
* Load a list of replies to the given post. This will load all descendants of the post.
* That is, all direct replies and replies to those replies etc.

View File

@ -328,6 +328,17 @@ $capabilities = array(
'manager' => CAP_ALLOW
)
),
'mod/forum:exportforum' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
)
),
'mod/forum:exportpost' => array(
'riskbitmask' => RISK_PERSONAL,

125
mod/forum/export.php Normal file
View File

@ -0,0 +1,125 @@
<?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/>.
/**
* Page to export forum discussions.
*
* @package mod_forum
* @copyright 2019 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_OUTPUT_BUFFERING', true);
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/dataformatlib.php');
$forumid = required_param('id', PARAM_INT);
$vaultfactory = mod_forum\local\container::get_vault_factory();
$managerfactory = mod_forum\local\container::get_manager_factory();
$legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
$forumvault = $vaultfactory->get_forum_vault();
$forum = $forumvault->get_from_id($forumid);
if (empty($forum)) {
throw new moodle_exception('Unable to find forum with id ' . $forumid);
}
$capabilitymanager = $managerfactory->get_capability_manager($forum);
if (!$capabilitymanager->can_export_forum($USER)) {
throw new moodle_exception('cannotexportforum', 'forum');
}
$course = $forum->get_course_record();
$coursemodule = $forum->get_course_module_record();
$cm = cm_info::create($coursemodule);
require_course_login($course, true, $cm);
$url = new moodle_url('/mod/forum/export.php');
$pagetitle = get_string('export', 'mod_forum');
$context = $forum->get_context();
$form = new mod_forum\form\export_form($url->out(false), [
'forum' => $forum
]);
if ($form->is_cancelled()) {
redirect(new moodle_url('/mod/forum/view.php', ['id' => $cm->id]));
} else if ($data = $form->get_data()) {
require_sesskey();
$dataformat = $data->format;
$discussionvault = $vaultfactory->get_discussion_vault();
$postvault = $vaultfactory->get_post_vault();
$discussionids = [];
if ($data->discussionids) {
$discussionids = $data->discussionids;
} else {
$discussions = $discussionvault->get_all_discussions_in_forum($forum);
$discussionids = array_map(function ($discussion) {
return $discussion->get_id();
}, $discussions);
}
if ($data->userids) {
$posts = $postvault->get_from_discussion_ids_and_user_ids($USER,
$discussionids,
$data->userids,
$capabilitymanager->can_view_any_private_reply($USER));
} else {
$posts = $postvault->get_from_discussion_ids($USER,
$discussionids,
$capabilitymanager->can_view_any_private_reply($USER));
}
$fields = ['id', 'discussion', 'parent', 'userid', 'created', 'modified', 'mailed', 'subject', 'message',
'messageformat', 'messagetrust', 'attachment', 'totalscore', 'mailnow', 'deleted', 'privatereplyto'];
$datamapper = $legacydatamapperfactory->get_post_data_mapper();
$exportdata = new ArrayObject($datamapper->to_legacy_objects($posts));
$iterator = $exportdata->getIterator();
require_once($CFG->libdir . '/dataformatlib.php');
$filename = clean_filename('discussion');
download_as_dataformat($filename, $dataformat, $fields, $iterator, function($exportdata) use ($fields) {
$data = $exportdata;
foreach ($fields as $field) {
// Convert any boolean fields to their integer equivalent for output.
if (is_bool($data->$field)) {
$data->$field = (int) $data->$field;
}
}
return $data;
});
die;
}
$PAGE->set_context($context);
$PAGE->set_url($url);
$PAGE->set_title($pagetitle);
$PAGE->set_pagelayout('admin');
$PAGE->set_heading($pagetitle);
echo $OUTPUT->header();
echo $OUTPUT->heading($pagetitle);
$form->display();
echo $OUTPUT->footer();

View File

@ -29,12 +29,14 @@ $string['addanewquestion'] = 'Add a new question';
$string['addanewtopic'] = 'Add a new topic';
$string['addtofavourites'] = 'Star this discussion';
$string['advancedsearch'] = 'Advanced search';
$string['alldiscussions'] = 'All discussions';
$string['allforums'] = 'All forums';
$string['allowdiscussions'] = 'Can a {$a} post to this forum?';
$string['allowsallsubscribe'] = 'This forum allows everyone to choose whether to subscribe or not';
$string['allowsdiscussions'] = 'This forum allows each person to start one discussion topic.';
$string['allsubscribe'] = 'Subscribe to all forums';
$string['allunsubscribe'] = 'Unsubscribe from all forums';
$string['allusers'] = 'All users';
$string['alreadyfirstpost'] = 'This is already the first post in the discussion';
$string['anyfile'] = 'Any file';
$string['areaattachment'] = 'Attachments';
@ -66,6 +68,7 @@ $string['cannotcreateinstanceforteacher'] = 'Could not create new course module
$string['cannotdeletepost'] = 'You can\'t delete this post!';
$string['cannotdeletediscussioninsinglediscussion'] = 'You cannot delete the first post in a single discussion';
$string['cannoteditposts'] = 'You can\'t edit other people\'s posts!';
$string['cannotexportforum'] = 'You cannot export this forum';
$string['cannotfinddiscussion'] = 'Could not find the discussion in this forum';
$string['cannotfindfirstpost'] = 'Could not find the first post in this forum';
$string['cannotfindorcreateforum'] = 'Could not find or create a main announcements forum for the site';
@ -242,6 +245,7 @@ $string['everyonecannowchoose'] = 'Everyone can now choose to be subscribed';
$string['everyoneisnowsubscribed'] = 'Everyone is now subscribed to this forum';
$string['everyoneissubscribed'] = 'Everyone is subscribed to this forum';
$string['existingsubscribers'] = 'Existing subscribers';
$string['export'] = 'Export';
$string['exportdiscussion'] = 'Export whole discussion to portfolio';
$string['exportattachmentname'] = 'Export attachment {$a} to portfolio';
$string['firstpost'] = 'First post';
@ -268,6 +272,7 @@ $string['forum:deleteanypost'] = 'Delete any posts (anytime)';
$string['forum:deleteownpost'] = 'Delete own posts (within deadline)';
$string['forum:editanypost'] = 'Edit any post';
$string['forum:exportdiscussion'] = 'Export whole discussion';
$string['forum:exportforum'] = 'Export forum';
$string['forum:exportownpost'] = 'Export own post';
$string['forum:exportpost'] = 'Export post';
$string['forumintro'] = 'Description';

View File

@ -5245,6 +5245,11 @@ function forum_extend_settings_navigation(settings_navigation $settingsnav, navi
$PAGE->cm->context = context_module::instance($PAGE->cm->instance);
}
$vaultfactory = mod_forum\local\container::get_vault_factory();
$managerfactory = mod_forum\local\container::get_manager_factory();
$forumvault = $vaultfactory->get_forum_vault();
$forumentity = $forumvault->get_from_id($forumobject->id);
$params = $PAGE->url->params();
if (!empty($params['d'])) {
$discussionid = $params['d'];
@ -5379,6 +5384,12 @@ function forum_extend_settings_navigation(settings_navigation $settingsnav, navi
$url = new moodle_url(rss_get_url($PAGE->cm->context->id, $userid, "mod_forum", $forumobject->id));
$forumnode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
}
$capabilitymanager = $managerfactory->get_capability_manager($forumentity);
if ($capabilitymanager->can_export_forum($USER)) {
$url = new moodle_url('/mod/forum/export.php', ['id' => $forumobject->id]);
$forumnode->add(get_string('export', 'mod_forum'), $url, navigation_node::TYPE_SETTING);
}
}
/**

View File

@ -0,0 +1,52 @@
{{!
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/form-user-selector-suggestion
Moodle template for the list of valid options in an autocomplate form element.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* fullname string Users full name
* email string user email field
Example context (json):
{
"fullname": "Admin User",
"extrafields": [
{
"name": "email",
"value": "admin@example.com"
},
{
"name": "phone1",
"value": "0123456789"
}
]
}
}}
<span>
<span>{{fullname}}</span>
{{#extrafields}}
<span><small>{{value}}</small></span>
{{/extrafields}}
</span>

View File

@ -194,6 +194,20 @@ class mod_forum_vaults_post_testcase extends advanced_testcase {
$this->assertArrayHasKey($post2->id, $entities);
$this->assertArrayHasKey($post3->id, $entities);
$this->assertArrayHasKey($post4->id, $entities);
// Test ordering by id descending.
$entities = $this->vault->get_from_discussion_ids($user, [$discussion1->id, $discussion2->id], false, 'id DESC');
$this->assertEquals($post4->id, array_values($entities)[0]->get_id());
$this->assertEquals($post3->id, array_values($entities)[1]->get_id());
$this->assertEquals($post2->id, array_values($entities)[2]->get_id());
$this->assertEquals($post1->id, array_values($entities)[3]->get_id());
// Test ordering by id ascending.
$entities = $this->vault->get_from_discussion_ids($user, [$discussion1->id, $discussion2->id], false, 'id ASC');
$this->assertEquals($post1->id, array_values($entities)[0]->get_id());
$this->assertEquals($post2->id, array_values($entities)[1]->get_id());
$this->assertEquals($post3->id, array_values($entities)[2]->get_id());
$this->assertEquals($post4->id, array_values($entities)[3]->get_id());
}
/**
@ -213,38 +227,85 @@ class mod_forum_vaults_post_testcase extends advanced_testcase {
[$student, $otherstudent] = $this->helper_create_users($course, 2, 'student');
[$teacher, $otherteacher] = $this->helper_create_users($course, 2, 'teacher');
[$discussion, $post] = $this->helper_post_to_forum($forum, $teacher);
$reply = $this->helper_post_to_discussion($forum, $discussion, $teacher, [
'privatereplyto' => $student->id,
]);
// Create the posts structure below.
// Forum:
// -> Post (student 1)
// ---> Post private reply (teacher 1)
// -> Otherpost (teacher 1)
// ---> Otherpost private reply (teacher 2)
// ---> Otherpost reply (student 1)
// ----> Otherpost reply private reply (teacher 1).
[$discussion, $post] = $this->helper_post_to_forum($forum, $student);
$postprivatereply = $this->helper_reply_to_post($post, $teacher, [
'privatereplyto' => $student->id
]);
[$otherdiscussion, $otherpost] = $this->helper_post_to_forum($forum, $teacher);
$otherpostprivatereply = $this->helper_reply_to_post($otherpost, $otherteacher, [
'privatereplyto' => $teacher->id,
]);
$otherpostreply = $this->helper_reply_to_post($otherpost, $student);
$otherpostreplyprivatereply = $this->helper_reply_to_post($otherpostreply, $teacher, [
'privatereplyto' => $student->id
]);
// The user is the author.
// Teacher 1. Request all posts from the vault, telling the vault that the teacher CAN see private replies made by anyone.
$entities = $this->vault->get_from_discussion_ids($teacher, [$discussion->id, $otherdiscussion->id], true);
$this->assertCount(3, $entities);
$this->assertCount(6, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($reply->id, $entities);
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// The user is the intended recipient.
// Student 1. Request all posts from the vault, telling the vault that the student CAN'T see private replies made by anyone.
// Teacher2's private reply to otherpost is omitted.
$entities = $this->vault->get_from_discussion_ids($student, [$discussion->id, $otherdiscussion->id], false);
$this->assertCount(3, $entities);
$this->assertCount(5, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($reply->id, $entities);
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostreply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// The user is another teacher..
// Student 1. Request all posts from the vault, telling the vault that student CAN see all private replies made.
// The private reply made by teacher 2 to otherpost is now included.
$entities = $this->vault->get_from_discussion_ids($student, [$discussion->id, $otherdiscussion->id], true);
$this->assertCount(6, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// Teacher 2. Request all posts from the vault, telling the vault that teacher2 CAN see all private replies made.
$entities = $this->vault->get_from_discussion_ids($otherteacher, [$discussion->id, $otherdiscussion->id], true);
$this->assertCount(6, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// Teacher 2. Request all posts from the vault, telling the vault that teacher2 CANNOT see all private replies made.
// The private replies not relating to teacher 2 directly are omitted.
$entities = $this->vault->get_from_discussion_ids($otherteacher, [$discussion->id, $otherdiscussion->id], false);
$this->assertCount(4, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreply->id, $entities);
// Student 2. Request all posts from the vault, telling the vault that student2 CAN'T see all private replies made.
// All private replies are omitted, as none relate to student2.
$entities = $this->vault->get_from_discussion_ids($otherstudent, [$discussion->id, $otherdiscussion->id], false);
$this->assertCount(3, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($reply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
// The user is a different student.
$entities = $this->vault->get_from_discussion_ids($otherstudent, [$discussion->id, $otherdiscussion->id], false);
$this->assertCount(2, $entities);
$this->assertArrayHasKey($post->id, $entities); // Order is not guaranteed, so just verify element existence.
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostreply->id, $entities);
}
/**
@ -877,4 +938,175 @@ class mod_forum_vaults_post_testcase extends advanced_testcase {
$this->assertEquals([], $this->vault->get_first_post_for_discussion_ids([]));
}
/**
* Test get_from_discussion_ids_and_user_ids.
*
* @covers ::get_from_discussion_ids_and_user_ids
* @covers ::<!public>
*/
public function test_get_from_discussion_ids_and_user_ids() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
[$user, $user2] = $this->helper_create_users($course, 2, 'student');
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion1, $post1] = $this->helper_post_to_forum($forum, $user);
$post2 = $this->helper_reply_to_post($post1, $user);
$post3 = $this->helper_reply_to_post($post1, $user);
[$discussion2, $post4] = $this->helper_post_to_forum($forum, $user);
$discussionids = [$discussion1->id, $discussion2->id];
$userids = [$user->id];
$entities = array_values($this->vault->get_from_discussion_ids_and_user_ids($user,
$discussionids,
$userids,
true,
'id ASC'));
$this->assertCount(4, $entities);
$this->assertEquals($post1->id, $entities[0]->get_id());
$this->assertEquals($post2->id, $entities[1]->get_id());
$this->assertEquals($post3->id, $entities[2]->get_id());
$this->assertEquals($post4->id, $entities[3]->get_id());
$entities = $this->vault->get_from_discussion_ids_and_user_ids($user, [$discussion1->id], $userids, false);
$this->assertCount(3, $entities);
$this->assertArrayHasKey($post1->id, $entities);
$this->assertArrayHasKey($post2->id, $entities);
$this->assertArrayHasKey($post3->id, $entities);
$entities = $this->vault->get_from_discussion_ids_and_user_ids($user, [$discussion1->id, $discussion2->id],
[$user->id, $user2->id], false);
$this->assertCount(4, $entities);
$this->assertArrayHasKey($post1->id, $entities);
$this->assertArrayHasKey($post2->id, $entities);
$this->assertArrayHasKey($post3->id, $entities);
$this->assertArrayHasKey($post4->id, $entities);
// Test ordering by id descending.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($user, [$discussion1->id, $discussion2->id],
[$user->id], false, 'id DESC');
$this->assertEquals($post4->id, array_values($entities)[0]->get_id());
$this->assertEquals($post3->id, array_values($entities)[1]->get_id());
$this->assertEquals($post2->id, array_values($entities)[2]->get_id());
$this->assertEquals($post1->id, array_values($entities)[3]->get_id());
// Test ordering by id ascending.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($user, [$discussion1->id, $discussion2->id],
[$user->id], false, 'id ASC');
$this->assertEquals($post1->id, array_values($entities)[0]->get_id());
$this->assertEquals($post2->id, array_values($entities)[1]->get_id());
$this->assertEquals($post3->id, array_values($entities)[2]->get_id());
$this->assertEquals($post4->id, array_values($entities)[3]->get_id());
}
/**
* Test get_from_discussion_ids_and_user_ids when no discussion ids were provided.
*
* @covers ::get_from_discussion_ids_and_user_ids
*/
public function test_get_from_discussion_ids_and_user_ids_empty() {
$this->resetAfterTest();
$datagenerator = $this->getDataGenerator();
$course = $datagenerator->create_course();
[$student1, $student2] = $this->helper_create_users($course, 2, 'student');
$forum = $datagenerator->create_module('forum', ['course' => $course->id]);
[$discussion, $post] = $this->helper_post_to_forum($forum, $student1);
$this->assertEquals([], $this->vault->get_from_discussion_ids_and_user_ids($student1, [], [], false));
$this->assertEquals([], $this->vault->get_from_discussion_ids_and_user_ids($student1, [$discussion->id], [], false));
$this->assertEquals([], $this->vault->get_from_discussion_ids_and_user_ids($student1, [], [$student2->id], false));
}
/**
* Ensure that selecting posts in a discussion only returns posts that the user can see, when considering private
* replies.
*
* @covers ::get_from_discussion_ids_and_user_ids
* @covers ::<!public>
*/
public function test_get_from_discussion_ids_and_user_ids_private_replies() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
]);
[$student, $otherstudent] = $this->helper_create_users($course, 2, 'student');
[$teacher, $otherteacher] = $this->helper_create_users($course, 2, 'teacher');
// Create the posts structure below.
// Forum:
// -> Post (student 1)
// ---> Post private reply (teacher 1)
// -> Otherpost (teacher 1)
// ---> Otherpost private reply (teacher 2)
// ---> Otherpost reply (student 1)
// ----> Otherpost reply private reply (teacher 1).
[$discussion, $post] = $this->helper_post_to_forum($forum, $student);
$postprivatereply = $this->helper_reply_to_post($post, $teacher, [
'privatereplyto' => $student->id
]);
[$otherdiscussion, $otherpost] = $this->helper_post_to_forum($forum, $teacher);
$otherpostprivatereply = $this->helper_reply_to_post($otherpost, $otherteacher, [
'privatereplyto' => $teacher->id,
]);
$otherpostreply = $this->helper_reply_to_post($otherpost, $student);
$otherpostreplyprivatereply = $this->helper_reply_to_post($otherpostreply, $teacher, [
'privatereplyto' => $student->id
]);
$userids = [$otherstudent->id, $teacher->id, $otherteacher->id];
$discussionids = [$discussion->id, $otherdiscussion->id];
// Teacher 1. Request all posts from the vault, telling the vault that the teacher CAN see private replies made by anyone.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($teacher, $discussionids, $userids, true);
$this->assertCount(4, $entities);
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// Student 1. Request all posts from the vault, telling the vault that the student CAN'T see private replies made by anyone.
// Teacher2's private reply to otherpost is omitted.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($student, $discussionids, $userids, false);
$this->assertCount(3, $entities);
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// Student 1. Request all posts from the vault, telling the vault that student CAN see all private replies made.
// The private reply made by teacher 2 to otherpost is now included.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($student, $discussionids, $userids, true);
$this->assertCount(4, $entities);
$this->assertArrayHasKey($postprivatereply->id, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// Teacher 2. Request all posts from the vault, telling the vault that teacher2 CAN see all private replies made.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($otherteacher, $discussionids, $userids, true);
$this->assertCount(4, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
$this->assertArrayHasKey($otherpostreplyprivatereply->id, $entities);
// Teacher 2. Request all posts from the vault, telling the vault that teacher2 CANNOT see all private replies made.
// The private replies not relating to teacher 2 directly are omitted.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($otherteacher, $discussionids, $userids, false);
$this->assertCount(2, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
$this->assertArrayHasKey($otherpostprivatereply->id, $entities);
// Student 2. Request all posts from the vault, telling the vault that student2 CAN'T see all private replies made.
// All private replies are omitted, as none relate to student2.
$entities = $this->vault->get_from_discussion_ids_and_user_ids($otherstudent, $discussionids, $userids, false);
$this->assertCount(1, $entities);
$this->assertArrayHasKey($otherpost->id, $entities);
}
}

View File

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

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2019092000.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2019092000.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.