Merge branch 'MDL-85706' of https://github.com/paulholden/moodle
This commit is contained in:
@@ -49,7 +49,9 @@ $string['contactblocked'] = 'Contact blocked';
|
||||
$string['contactrequests'] = 'Contact requests';
|
||||
$string['contactrequestsent'] = 'Contact request sent';
|
||||
$string['contacts'] = 'Contacts';
|
||||
$string['conversation'] = 'Conversation';
|
||||
$string['conversationactions'] = 'Conversation actions menu';
|
||||
$string['conversationtype'] = 'Type';
|
||||
$string['decline'] = 'Decline';
|
||||
$string['defaultmessageoutputs'] = 'Notification settings';
|
||||
$string['deleteallconfirm'] = "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.";
|
||||
@@ -214,6 +216,7 @@ $string['processorsettings'] = 'Processor settings';
|
||||
$string['providerenabled'] = 'Sending "{$a}" enabled status';
|
||||
$string['providerprocesorislocked'] = '"{$a->provider}" on "{$a->processor}" is locked on';
|
||||
$string['providerprocesorisdisallowed'] = '"{$a->provider}" on "{$a->processor}" is locked off';
|
||||
$string['recipient'] = 'Recipient';
|
||||
$string['removecontact'] = 'Remove contact';
|
||||
$string['removecontactconfirm'] = 'Are you sure you want to remove {$a} from your contacts?';
|
||||
$string['removecoursefilter'] = 'Remove filter for course {$a}';
|
||||
|
||||
@@ -14,16 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains class used to return information to display for the message area.
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2016 Mark Nelson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_message;
|
||||
|
||||
use core\{clock, di};
|
||||
use core_favourites\local\entity\favourite;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
@@ -33,6 +26,7 @@ require_once($CFG->dirroot . '/lib/messagelib.php');
|
||||
/**
|
||||
* Class used to return information to display for the message area.
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2016 Mark Nelson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -1235,7 +1229,7 @@ class api {
|
||||
$mua->userid = $userid;
|
||||
$mua->messageid = $message->id;
|
||||
$mua->action = self::MESSAGE_ACTION_DELETED;
|
||||
$mua->timecreated = time();
|
||||
$mua->timecreated = di::get(clock::class)->time();
|
||||
$mua->id = $DB->insert_record('message_user_actions', $mua);
|
||||
|
||||
\core\event\message_deleted::create_from_ids($userid, $USER->id,
|
||||
@@ -1650,7 +1644,7 @@ class api {
|
||||
$eventdata->fullmessageformat = $format;
|
||||
$eventdata->smallmessage = $message; // Store the message unfiltered. Clean up on output.
|
||||
|
||||
$eventdata->timecreated = time();
|
||||
$eventdata->timecreated = di::get(clock::class)->time();
|
||||
$eventdata->notification = 0;
|
||||
// Custom data for event.
|
||||
$customdata = [
|
||||
@@ -1894,7 +1888,7 @@ class api {
|
||||
global $DB;
|
||||
|
||||
if (is_null($timeread)) {
|
||||
$timeread = time();
|
||||
$timeread = di::get(clock::class)->time();
|
||||
}
|
||||
|
||||
$mua = new \stdClass();
|
||||
@@ -1934,7 +1928,7 @@ class api {
|
||||
global $DB;
|
||||
|
||||
if (is_null($timeread)) {
|
||||
$timeread = time();
|
||||
$timeread = di::get(clock::class)->time();
|
||||
}
|
||||
|
||||
if (is_null($notification->timeread)) {
|
||||
@@ -2008,7 +2002,7 @@ class api {
|
||||
$mua->userid = $userid;
|
||||
$mua->messageid = $messageid;
|
||||
$mua->action = self::MESSAGE_ACTION_DELETED;
|
||||
$mua->timecreated = time();
|
||||
$mua->timecreated = di::get(clock::class)->time();
|
||||
$mua->id = $DB->insert_record('message_user_actions', $mua);
|
||||
|
||||
// Trigger event for deleting a message.
|
||||
@@ -2139,7 +2133,7 @@ class api {
|
||||
$conversation->itemid = $itemid;
|
||||
$conversation->contextid = $contextid;
|
||||
$conversation->enabled = $enabled;
|
||||
$conversation->timecreated = time();
|
||||
$conversation->timecreated = di::get(clock::class)->time();
|
||||
$conversation->timemodified = $conversation->timecreated;
|
||||
$conversation->id = $DB->insert_record('message_conversations', $conversation);
|
||||
|
||||
@@ -2149,7 +2143,7 @@ class api {
|
||||
$member = new \stdClass();
|
||||
$member->conversationid = $conversation->id;
|
||||
$member->userid = $userid;
|
||||
$member->timecreated = time();
|
||||
$member->timecreated = di::get(clock::class)->time();
|
||||
$member->id = $DB->insert_record('message_conversation_members', $member);
|
||||
|
||||
$arrmembers[] = $member;
|
||||
@@ -2216,7 +2210,7 @@ class api {
|
||||
$request = new \stdClass();
|
||||
$request->userid = $userid;
|
||||
$request->requesteduserid = $requesteduserid;
|
||||
$request->timecreated = time();
|
||||
$request->timecreated = di::get(clock::class)->time();
|
||||
|
||||
$request->id = $DB->insert_record('message_contact_requests', $request);
|
||||
|
||||
@@ -2359,7 +2353,7 @@ class api {
|
||||
$messagecontact = new \stdClass();
|
||||
$messagecontact->userid = $userid;
|
||||
$messagecontact->contactid = $contactid;
|
||||
$messagecontact->timecreated = time();
|
||||
$messagecontact->timecreated = di::get(clock::class)->time();
|
||||
$messagecontact->id = $DB->insert_record('message_contacts', $messagecontact);
|
||||
|
||||
$eventparams = [
|
||||
@@ -2408,7 +2402,7 @@ class api {
|
||||
$blocked = new \stdClass();
|
||||
$blocked->userid = $userid;
|
||||
$blocked->blockeduserid = $usertoblockid;
|
||||
$blocked->timecreated = time();
|
||||
$blocked->timecreated = di::get(clock::class)->time();
|
||||
$blocked->id = $DB->insert_record('message_users_blocked', $blocked);
|
||||
|
||||
// Trigger event for blocking a contact.
|
||||
@@ -2651,7 +2645,7 @@ class api {
|
||||
$member = new \stdClass();
|
||||
$member->conversationid = $convid;
|
||||
$member->userid = $userid;
|
||||
$member->timecreated = time();
|
||||
$member->timecreated = di::get(clock::class)->time();
|
||||
$members[] = $member;
|
||||
}
|
||||
|
||||
@@ -2752,7 +2746,7 @@ class api {
|
||||
$conversation = new \stdClass();
|
||||
$conversation->id = $conversationid;
|
||||
$conversation->enabled = self::MESSAGE_CONVERSATION_ENABLED;
|
||||
$conversation->timemodified = time();
|
||||
$conversation->timemodified = di::get(clock::class)->time();
|
||||
$DB->update_record('message_conversations', $conversation);
|
||||
}
|
||||
|
||||
@@ -2768,7 +2762,7 @@ class api {
|
||||
$conversation = new \stdClass();
|
||||
$conversation->id = $conversationid;
|
||||
$conversation->enabled = self::MESSAGE_CONVERSATION_DISABLED;
|
||||
$conversation->timemodified = time();
|
||||
$conversation->timemodified = di::get(clock::class)->time();
|
||||
$DB->update_record('message_conversations', $conversation);
|
||||
}
|
||||
|
||||
@@ -2785,7 +2779,7 @@ class api {
|
||||
if ($conversation = $DB->get_record('message_conversations', array('id' => $conversationid))) {
|
||||
if ($name <> $conversation->name) {
|
||||
$conversation->name = $name;
|
||||
$conversation->timemodified = time();
|
||||
$conversation->timemodified = di::get(clock::class)->time();
|
||||
$DB->update_record('message_conversations', $conversation);
|
||||
}
|
||||
}
|
||||
@@ -2886,7 +2880,7 @@ class api {
|
||||
$mutedconversation->userid = $userid;
|
||||
$mutedconversation->conversationid = $conversationid;
|
||||
$mutedconversation->action = self::CONVERSATION_ACTION_MUTED;
|
||||
$mutedconversation->timecreated = time();
|
||||
$mutedconversation->timecreated = di::get(clock::class)->time();
|
||||
|
||||
$DB->insert_record('message_conversation_actions', $mutedconversation);
|
||||
}
|
||||
|
||||
@@ -14,17 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains helper class for the message area.
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2016 Mark Nelson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_message;
|
||||
|
||||
use core\{clock, di};
|
||||
use DOMDocument;
|
||||
use stdclass;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@@ -33,6 +27,7 @@ require_once($CFG->dirroot . '/message/lib.php');
|
||||
/**
|
||||
* Helper class for the message area.
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2016 Mark Nelson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -254,7 +249,7 @@ class helper {
|
||||
if (isset($CFG->block_online_users_timetosee)) {
|
||||
$timetoshowusers = $CFG->block_online_users_timetosee * 60;
|
||||
}
|
||||
$time = time() - $timetoshowusers;
|
||||
$time = di::get(clock::class)->time() - $timetoshowusers;
|
||||
|
||||
return $lastaccess >= $time;
|
||||
}
|
||||
@@ -608,7 +603,7 @@ class helper {
|
||||
'contactrequestcount' => $requestcount,
|
||||
'loggedinuser' => [
|
||||
'id' => $USER->id,
|
||||
'midnight' => usergetmidnight(time())
|
||||
'midnight' => usergetmidnight(di::get(clock::class)->time()),
|
||||
],
|
||||
// The starting timeout value for message polling.
|
||||
'messagepollmin' => $CFG->messagingminpoll ?? MESSAGE_DEFAULT_MIN_POLL_IN_SECONDS,
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_message\reportbuilder\datasource;
|
||||
|
||||
use core\lang_string;
|
||||
use core_message\reportbuilder\local\entities\{conversation, message};
|
||||
use core_reportbuilder\datasource;
|
||||
use core_reportbuilder\local\entities\user;
|
||||
use core_reportbuilder\local\filters\text;
|
||||
use core_reportbuilder\local\helpers\database;
|
||||
|
||||
/**
|
||||
* Messages datasource
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2025 Paul Holden <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class messages extends datasource {
|
||||
/**
|
||||
* Return user friendly name of the datasource
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name(): string {
|
||||
return get_string('messages', 'core_message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise report
|
||||
*/
|
||||
protected function initialise(): void {
|
||||
// Message.
|
||||
$messageentity = new message();
|
||||
$messagealias = $messageentity->get_table_alias('messages');
|
||||
|
||||
$this->set_main_table('messages', $messagealias);
|
||||
$this->add_entity($messageentity);
|
||||
|
||||
// Conversation.
|
||||
$conversationentity = new conversation();
|
||||
$conversationalias = $conversationentity->get_table_alias('message_conversations');
|
||||
$this->add_entity($conversationentity
|
||||
->add_join("LEFT JOIN {message_conversations} {$conversationalias}
|
||||
ON {$conversationalias}.id = {$messagealias}.conversationid"));
|
||||
|
||||
// Author.
|
||||
$authorentity = new user();
|
||||
$authoralias = $authorentity->get_table_alias('user');
|
||||
$this->add_entity($authorentity
|
||||
->add_join("LEFT JOIN {user} {$authoralias} ON {$authoralias}.id = {$messagealias}.useridfrom"));
|
||||
|
||||
// Recipient.
|
||||
$recipiententity = new user();
|
||||
$recipientalias = $recipiententity->get_table_alias('user');
|
||||
$membersalias = database::generate_alias();
|
||||
$this->add_entity($recipiententity
|
||||
->set_entity_name('recipient')
|
||||
->set_entity_title(new lang_string('recipient', 'core_message'))
|
||||
->add_joins($conversationentity->get_joins())
|
||||
->add_joins([
|
||||
"LEFT JOIN {message_conversation_members} {$membersalias}
|
||||
ON {$membersalias}.conversationid = {$conversationalias}.id
|
||||
AND {$membersalias}.userid != {$messagealias}.useridfrom",
|
||||
"LEFT JOIN {user} {$recipientalias} ON {$recipientalias}.id = {$membersalias}.userid",
|
||||
]));
|
||||
|
||||
// Add all columns/filters/conditions from entities to be available in custom reports.
|
||||
$this->add_all_from_entities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the columns that will be added to the report as part of default setup
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_default_columns(): array {
|
||||
return [
|
||||
'user:fullname',
|
||||
'recipient:fullname',
|
||||
'message:message',
|
||||
'message:timecreated',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default sorting that will be added to the report upon creation
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function get_default_column_sorting(): array {
|
||||
return [
|
||||
'user:fullname' => SORT_ASC,
|
||||
'recipient:fullname' => SORT_ASC,
|
||||
'message:timecreated' => SORT_ASC,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filters that will be added to the report upon creation
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_default_filters(): array {
|
||||
return [
|
||||
'user:fullname',
|
||||
'recipient:fullname',
|
||||
'message:message',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the conditions that will be added to the report upon creation
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_default_conditions(): array {
|
||||
return [
|
||||
'message:message',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the condition values that will be set for the report upon creation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_default_condition_values(): array {
|
||||
return [
|
||||
'message:message_operator' => text::IS_NOT_EMPTY,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_message\reportbuilder\local\entities;
|
||||
|
||||
use core\lang_string;
|
||||
use core_message\api;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
|
||||
use core_reportbuilder\local\helpers\format;
|
||||
use core_reportbuilder\local\report\{column, filter};
|
||||
|
||||
/**
|
||||
* Conversation entity
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2025 Paul Holden <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class conversation extends base {
|
||||
/**
|
||||
* Database tables that this entity uses
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_default_tables(): array {
|
||||
return [
|
||||
'message_conversations',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default title for this entity
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
protected function get_default_entity_title(): lang_string {
|
||||
return new lang_string('conversation', 'core_message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the entity
|
||||
*
|
||||
* @return base
|
||||
*/
|
||||
public function initialise(): base {
|
||||
$columns = $this->get_all_columns();
|
||||
foreach ($columns as $column) {
|
||||
$this->add_column($column);
|
||||
}
|
||||
|
||||
// All the filters defined by the entity can also be used as conditions.
|
||||
$filters = $this->get_all_filters();
|
||||
foreach ($filters as $filter) {
|
||||
$this
|
||||
->add_filter($filter)
|
||||
->add_condition($filter);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all available columns
|
||||
*
|
||||
* @return column[]
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
$conversationalias = $this->get_table_alias('message_conversations');
|
||||
|
||||
// Type.
|
||||
$columns[] = (new column(
|
||||
'type',
|
||||
new lang_string('conversationtype', 'core_message'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_field("{$conversationalias}.type")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function (?string $type): string {
|
||||
$types = [
|
||||
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new lang_string('individualconversations', 'core_message'),
|
||||
api::MESSAGE_CONVERSATION_TYPE_GROUP => new lang_string('groupconversations', 'core_message'),
|
||||
api::MESSAGE_CONVERSATION_TYPE_SELF => new lang_string('selfconversation', 'core_message'),
|
||||
];
|
||||
if ($type === null || !array_key_exists($type, $types)) {
|
||||
return '';
|
||||
}
|
||||
return (string) $types[$type];
|
||||
});
|
||||
|
||||
// Name.
|
||||
$columns[] = (new column(
|
||||
'name',
|
||||
new lang_string('name'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_field("{$conversationalias}.name")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function (?string $name): string {
|
||||
if ($name === null) {
|
||||
return '';
|
||||
}
|
||||
return format_string($name);
|
||||
});
|
||||
|
||||
// Enabled.
|
||||
$columns[] = (new column(
|
||||
'enabled',
|
||||
new lang_string('enabled', 'core_message'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_BOOLEAN)
|
||||
->add_field("{$conversationalias}.enabled")
|
||||
->set_is_sortable(true)
|
||||
->add_callback([format::class, 'boolean_as_text']);
|
||||
|
||||
// Time created.
|
||||
$columns[] = (new column(
|
||||
'timecreated',
|
||||
new lang_string('timecreated', 'core_reportbuilder'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TIMESTAMP)
|
||||
->add_field("{$conversationalias}.timecreated")
|
||||
->set_is_sortable(true)
|
||||
->add_callback([format::class, 'userdate']);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all available filters
|
||||
*
|
||||
* @return filter[]
|
||||
*/
|
||||
protected function get_all_filters(): array {
|
||||
$conversationalias = $this->get_table_alias('message_conversations');
|
||||
|
||||
// Type.
|
||||
$filters[] = (new filter(
|
||||
select::class,
|
||||
'type',
|
||||
new lang_string('conversationtype', 'core_message'),
|
||||
$this->get_entity_name(),
|
||||
"{$conversationalias}.type",
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options([
|
||||
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new lang_string('individualconversations', 'core_message'),
|
||||
api::MESSAGE_CONVERSATION_TYPE_GROUP => new lang_string('groupconversations', 'core_message'),
|
||||
api::MESSAGE_CONVERSATION_TYPE_SELF => new lang_string('selfconversation', 'core_message'),
|
||||
]);
|
||||
|
||||
// Name.
|
||||
$filters[] = (new filter(
|
||||
text::class,
|
||||
'name',
|
||||
new lang_string('name'),
|
||||
$this->get_entity_name(),
|
||||
"{$conversationalias}.name",
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Enabled.
|
||||
$filters[] = (new filter(
|
||||
boolean_select::class,
|
||||
'enabled',
|
||||
new lang_string('enabled', 'core_message'),
|
||||
$this->get_entity_name(),
|
||||
"{$conversationalias}.enabled",
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Time created.
|
||||
$filters[] = (new filter(
|
||||
date::class,
|
||||
'timecreated',
|
||||
new lang_string('timecreated', 'core_reportbuilder'),
|
||||
$this->get_entity_name(),
|
||||
"{$conversationalias}.timecreated",
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
return $filters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_message\reportbuilder\local\entities;
|
||||
|
||||
use core\lang_string;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
use core_reportbuilder\local\filters\{date, text};
|
||||
use core_reportbuilder\local\helpers\format;
|
||||
use core_reportbuilder\local\report\{column, filter};
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Message entity
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2025 Paul Holden <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class message extends base {
|
||||
/**
|
||||
* Database tables that this entity uses
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_default_tables(): array {
|
||||
return [
|
||||
'messages',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default title for this entity
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
protected function get_default_entity_title(): lang_string {
|
||||
return new lang_string('message', 'core_message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the entity
|
||||
*
|
||||
* @return base
|
||||
*/
|
||||
public function initialise(): base {
|
||||
$columns = $this->get_all_columns();
|
||||
foreach ($columns as $column) {
|
||||
$this->add_column($column);
|
||||
}
|
||||
|
||||
// All the filters defined by the entity can also be used as conditions.
|
||||
$filters = $this->get_all_filters();
|
||||
foreach ($filters as $filter) {
|
||||
$this
|
||||
->add_filter($filter)
|
||||
->add_condition($filter);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all available columns
|
||||
*
|
||||
* @return column[]
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
$messagealias = $this->get_table_alias('messages');
|
||||
|
||||
// Subject.
|
||||
$columns[] = (new column(
|
||||
'subject',
|
||||
new lang_string('subject'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_field("{$messagealias}.subject")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function (?string $subject): string {
|
||||
if ($subject === null) {
|
||||
return '';
|
||||
}
|
||||
return format_string($subject);
|
||||
});
|
||||
|
||||
// Message.
|
||||
$columns[] = (new column(
|
||||
'message',
|
||||
new lang_string('message', 'core_message'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_fields("{$messagealias}.fullmessage, {$messagealias}.fullmessageformat, {$messagealias}.fullmessagetrust")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function (?string $fullmessage, stdClass $message): string {
|
||||
if ($fullmessage === null) {
|
||||
return '';
|
||||
}
|
||||
return format_text($fullmessage, $message->fullmessageformat, ['trusted' => $message->fullmessagetrust]);
|
||||
});
|
||||
|
||||
// Time created.
|
||||
$columns[] = (new column(
|
||||
'timecreated',
|
||||
new lang_string('timecreated', 'core_reportbuilder'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TIMESTAMP)
|
||||
->add_field("{$messagealias}.timecreated")
|
||||
->set_is_sortable(true)
|
||||
->add_callback([format::class, 'userdate']);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all available filters
|
||||
*
|
||||
* @return filter[]
|
||||
*/
|
||||
protected function get_all_filters(): array {
|
||||
$messagealias = $this->get_table_alias('messages');
|
||||
|
||||
// Subject.
|
||||
$filters[] = (new filter(
|
||||
text::class,
|
||||
'subject',
|
||||
new lang_string('subject'),
|
||||
$this->get_entity_name(),
|
||||
"{$messagealias}.subject",
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Message.
|
||||
$filters[] = (new filter(
|
||||
text::class,
|
||||
'message',
|
||||
new lang_string('message', 'core_message'),
|
||||
$this->get_entity_name(),
|
||||
"{$messagealias}.fullmessage",
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Time created.
|
||||
$filters[] = (new filter(
|
||||
date::class,
|
||||
'timecreated',
|
||||
new lang_string('timecreated', 'core_reportbuilder'),
|
||||
$this->get_entity_name(),
|
||||
"{$messagealias}.timecreated",
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
return $filters;
|
||||
}
|
||||
}
|
||||
@@ -14,21 +14,15 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains a helper class providing util methods for testing.
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2018 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_message\tests;
|
||||
|
||||
use core\{clock, di};
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* The helper class providing util methods for testing.
|
||||
*
|
||||
* @package core_message
|
||||
* @copyright 2018 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -57,7 +51,7 @@ class helper {
|
||||
global $DB;
|
||||
|
||||
if (empty($time)) {
|
||||
$time = time();
|
||||
$time = di::get(clock::class)->time();
|
||||
}
|
||||
|
||||
if ($notification) {
|
||||
@@ -122,7 +116,7 @@ class helper {
|
||||
global $DB;
|
||||
$conversationrec = $DB->get_record('message_conversations', ['id' => $convid], 'id', MUST_EXIST);
|
||||
$conversationid = $conversationrec->id;
|
||||
$time = $time ?? time();
|
||||
$time = $time ?? di::get(clock::class)->time();
|
||||
$record = new \stdClass();
|
||||
$record->useridfrom = $userfrom->id;
|
||||
$record->conversationid = $conversationid;
|
||||
@@ -157,7 +151,7 @@ class helper {
|
||||
$record->subject = 'No subject';
|
||||
$record->fullmessage = $message;
|
||||
$record->smallmessage = $message;
|
||||
$record->timecreated = $timecreated ? $timecreated : time();
|
||||
$record->timecreated = $timecreated ?: di::get(clock::class)->time();
|
||||
$record->customdata = json_encode(['datakey' => 'data']);
|
||||
|
||||
return $DB->insert_record('notifications', $record);
|
||||
@@ -188,8 +182,8 @@ class helper {
|
||||
$record->subject = 'No subject';
|
||||
$record->fullmessage = $message;
|
||||
$record->smallmessage = $message;
|
||||
$record->timecreated = $timecreated ? $timecreated : time();
|
||||
$record->timeread = $timeread ? $timeread : time();
|
||||
$record->timecreated = $timecreated ?: di::get(clock::class)->time();
|
||||
$record->timeread = $timeread ?: di::get(clock::class)->time();
|
||||
|
||||
$record->id = $DB->insert_record('notifications', $record);
|
||||
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_message\reportbuilder\datasource;
|
||||
|
||||
use core\clock;
|
||||
use core_message\api;
|
||||
use core_message\tests\helper;
|
||||
use core_reportbuilder_generator;
|
||||
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
|
||||
use core_reportbuilder\tests\core_reportbuilder_testcase;
|
||||
|
||||
/**
|
||||
* Unit tests for messages datasource
|
||||
*
|
||||
* @package core_message
|
||||
* @covers \core_message\reportbuilder\datasource\messages
|
||||
* @copyright 2025 Paul Holden <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class messages_test extends core_reportbuilder_testcase {
|
||||
/** @var clock $clock */
|
||||
private readonly clock $clock;
|
||||
|
||||
/**
|
||||
* Mock the clock
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->clock = $this->mock_clock_with_frozen(1622502000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test default datasource
|
||||
*/
|
||||
public function test_datasource_default(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Test subject.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$userone = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']);
|
||||
$usertwo = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Morris', 'lastname' => 'Moo']);
|
||||
$userthree = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Aaron', 'lastname' => 'Ant']);
|
||||
|
||||
$groupconversation = api::create_conversation(
|
||||
api::MESSAGE_CONVERSATION_TYPE_GROUP,
|
||||
[$userone->id, $usertwo->id, $userthree->id],
|
||||
);
|
||||
helper::send_fake_message_to_conversation($userone, $groupconversation->id, 'Are you somewhere feeling lonely?');
|
||||
|
||||
$this->clock->bump(HOURSECS);
|
||||
|
||||
$privateconversation = api::create_conversation(
|
||||
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||
[$userone->id, $usertwo->id],
|
||||
);
|
||||
helper::send_fake_message_to_conversation($usertwo, $privateconversation->id, 'Or is someone loving you?');
|
||||
|
||||
/** @var core_reportbuilder_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
|
||||
$report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 1]);
|
||||
|
||||
$content = $this->get_custom_report_content($report->get('id'));
|
||||
|
||||
// Default columns are author, recipient, message, time created. Sorted by author, recipient, time created.
|
||||
$this->assertEquals([
|
||||
[
|
||||
fullname($usertwo),
|
||||
fullname($userone),
|
||||
'<div class="text_to_html">Or is someone loving you?</div>',
|
||||
'Tuesday, 1 June 2021, 8:00 AM',
|
||||
],
|
||||
[
|
||||
fullname($userone),
|
||||
fullname($userthree),
|
||||
'<div class="text_to_html">Are you somewhere feeling lonely?</div>',
|
||||
'Tuesday, 1 June 2021, 7:00 AM',
|
||||
],
|
||||
[
|
||||
fullname($userone),
|
||||
fullname($usertwo),
|
||||
'<div class="text_to_html">Are you somewhere feeling lonely?</div>',
|
||||
'Tuesday, 1 June 2021, 7:00 AM',
|
||||
],
|
||||
], array_map('array_values', $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test datasource columns that aren't added by default
|
||||
*/
|
||||
public function test_datasource_non_default_columns(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Test subject.
|
||||
$userone = $this->getDataGenerator()->create_user();
|
||||
$usertwo = $this->getDataGenerator()->create_user();
|
||||
|
||||
$conversation = api::create_conversation(
|
||||
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||
[$userone->id, $usertwo->id],
|
||||
'My conversation',
|
||||
);
|
||||
helper::send_fake_message_to_conversation($userone, $conversation->id);
|
||||
|
||||
/** @var core_reportbuilder_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
|
||||
$report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 0]);
|
||||
|
||||
// Message.
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'message:subject']);
|
||||
|
||||
// Conversation.
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:type']);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:name']);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:enabled']);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:timecreated']);
|
||||
|
||||
$content = $this->get_custom_report_content($report->get('id'));
|
||||
|
||||
$this->assertEquals([
|
||||
[
|
||||
'No subject',
|
||||
'Private',
|
||||
'My conversation',
|
||||
'Yes',
|
||||
'Tuesday, 1 June 2021, 7:00 AM',
|
||||
],
|
||||
], array_map('array_values', $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for {@see test_datasource_filters}
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function datasource_filters_provider(): array {
|
||||
return [
|
||||
// Message.
|
||||
'Message subject' => ['message:subject', [
|
||||
'message:subject_operator' => text::IS_EQUAL_TO,
|
||||
'message:subject_value' => 'No subject',
|
||||
], true],
|
||||
'Message subject (no match)' => ['message:subject', [
|
||||
'message:subject_operator' => text::IS_EQUAL_TO,
|
||||
'message:subject_value' => 'Another subject',
|
||||
], false],
|
||||
'Message content' => ['message:message', [
|
||||
'message:message_operator' => text::IS_EQUAL_TO,
|
||||
'message:message_value' => 'Hi',
|
||||
], true],
|
||||
'Message content (no match)' => ['message:message', [
|
||||
'message:message_operator' => text::IS_EQUAL_TO,
|
||||
'message:message_value' => 'Bye',
|
||||
], false],
|
||||
'Message time created' => ['message:timecreated', [
|
||||
'message:timecreated_operator' => date::DATE_RANGE,
|
||||
'message:timecreated_from' => 1622502000,
|
||||
], true],
|
||||
'Message time created (no match)' => ['message:timecreated', [
|
||||
'message:timecreated_operator' => date::DATE_RANGE,
|
||||
'message:timecreated_to' => 1622502000,
|
||||
], false],
|
||||
|
||||
// Conversation.
|
||||
'Conversation type' => ['conversation:type', [
|
||||
'conversation:type_operator' => select::EQUAL_TO,
|
||||
'conversation:type_value' => api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||
], true],
|
||||
'Conversation type (no match)' => ['conversation:type', [
|
||||
'conversation:type_operator' => select::EQUAL_TO,
|
||||
'conversation:type_value' => api::MESSAGE_CONVERSATION_TYPE_GROUP,
|
||||
], false],
|
||||
'Conversation name' => ['conversation:name', [
|
||||
'conversation:name_operator' => text::IS_EQUAL_TO,
|
||||
'conversation:name_value' => 'My conversation',
|
||||
], true],
|
||||
'Conversation name (no match)' => ['conversation:name', [
|
||||
'conversation:name_operator' => text::IS_EQUAL_TO,
|
||||
'conversation:name_value' => 'Another conversation',
|
||||
], false],
|
||||
'Conversation enabled' => ['conversation:enabled', [
|
||||
'conversation:enabled_operator' => boolean_select::CHECKED,
|
||||
], true],
|
||||
'Conversation enabled (no match)' => ['conversation:enabled', [
|
||||
'conversation:enabled_operator' => boolean_select::NOT_CHECKED,
|
||||
], false],
|
||||
'Conversation time created' => ['conversation:timecreated', [
|
||||
'conversation:timecreated_operator' => date::DATE_RANGE,
|
||||
'conversation:timecreated_from' => 1622502000,
|
||||
], true],
|
||||
'Conversation time created (no match)' => ['conversation:timecreated', [
|
||||
'conversation:timecreated_operator' => date::DATE_RANGE,
|
||||
'conversation:timecreated_to' => 1622502000,
|
||||
], false],
|
||||
|
||||
// Author.
|
||||
'User firstname' => ['user:firstname', [
|
||||
'user:firstname_operator' => text::IS_EQUAL_TO,
|
||||
'user:firstname_value' => 'Zoe',
|
||||
], true],
|
||||
'User firstname (no match)' => ['user:firstname', [
|
||||
'user:firstname_operator' => text::IS_EQUAL_TO,
|
||||
'user:firstname_value' => 'Aaron',
|
||||
], false],
|
||||
|
||||
// Recipient.
|
||||
'Recipient firstname' => ['recipient:firstname', [
|
||||
'recipient:firstname_operator' => text::IS_EQUAL_TO,
|
||||
'recipient:firstname_value' => 'Aaron',
|
||||
], true],
|
||||
'Recipient firstname (no match)' => ['recipient:firstname', [
|
||||
'recipient:firstname_operator' => text::IS_EQUAL_TO,
|
||||
'recipient:firstname_value' => 'Zoe',
|
||||
], false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test datasource filters
|
||||
*
|
||||
* @param string $filtername
|
||||
* @param array $filtervalues
|
||||
* @param bool $expectmatch
|
||||
*
|
||||
* @dataProvider datasource_filters_provider
|
||||
*/
|
||||
public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Test subject.
|
||||
$userone = $this->getDataGenerator()->create_user(['firstname' => 'Zoe']);
|
||||
$usertwo = $this->getDataGenerator()->create_user(['firstname' => 'Aaron']);
|
||||
|
||||
$conversation = api::create_conversation(
|
||||
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||
[$userone->id, $usertwo->id],
|
||||
'My conversation',
|
||||
);
|
||||
helper::send_fake_message_to_conversation($userone, $conversation->id, 'Hi');
|
||||
|
||||
/** @var core_reportbuilder_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
|
||||
|
||||
// Create report containing single column, and given filter.
|
||||
$report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 0]);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'message:subject']);
|
||||
|
||||
// Add filter, set it's values.
|
||||
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
|
||||
$content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
|
||||
|
||||
if ($expectmatch) {
|
||||
$this->assertEquals([
|
||||
['No subject'],
|
||||
], array_map('array_values', $content));
|
||||
} else {
|
||||
$this->assertEmpty($content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stress test datasource
|
||||
*
|
||||
* In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
|
||||
*/
|
||||
public function test_stress_datasource(): void {
|
||||
if (!PHPUNIT_LONGTEST) {
|
||||
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
|
||||
}
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Test subject.
|
||||
$userone = $this->getDataGenerator()->create_user();
|
||||
$usertwo = $this->getDataGenerator()->create_user();
|
||||
|
||||
$conversation = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [$userone->id, $usertwo->id]);
|
||||
helper::send_fake_message_to_conversation($userone, $conversation->id);
|
||||
|
||||
$this->datasource_stress_test_columns(messages::class);
|
||||
$this->datasource_stress_test_columns_aggregation(messages::class);
|
||||
$this->datasource_stress_test_conditions(messages::class, 'message:subject');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user