Merge branch 'MDL-48212-master' of git://github.com/ankitagarwal/moodle

This commit is contained in:
David Monllao 2015-02-10 08:54:13 +08:00
commit f01845c2a6
5 changed files with 480 additions and 15 deletions

View File

@ -50,13 +50,19 @@ class manager {
*
* NOTE: to be used from message_send() only.
*
* @param \stdClass $eventdata fully prepared event data for processors
* @param \stdClass|\core\message\message $eventdata fully prepared event data for processors
* @param \stdClass $savemessage the message saved in 'message' table
* @param array $processorlist list of processors for target user
* @return int $messageid the id from 'message' or 'message_read' table (false is not returned)
*/
public static function send_message(\stdClass $eventdata, \stdClass $savemessage, array $processorlist) {
public static function send_message($eventdata, \stdClass $savemessage, array $processorlist) {
global $CFG;
if (!($eventdata instanceof \stdClass) && !($eventdata instanceof message)) {
// Not a valid object.
throw new \coding_exception('Message should be of type stdClass or \core\message\message');
}
require_once($CFG->dirroot.'/message/lib.php'); // This is most probably already included from messagelib.php file.
if (empty($processorlist)) {
@ -85,12 +91,13 @@ class manager {
/**
* Send message to message processors.
*
* @param \stdClass $eventdata
* @param \stdClass|\core\message\message $eventdata
* @param \stdClass $savemessage
* @param array $processorlist
* @return int $messageid
*/
protected static function send_message_to_processors(\stdClass $eventdata, \stdClass $savemessage, array $processorlist) {
protected static function send_message_to_processors($eventdata, \stdClass $savemessage, array
$processorlist) {
global $CFG, $DB;
// We cannot communicate with external systems in DB transactions,
@ -114,7 +121,9 @@ class manager {
$failed = false;
foreach ($processorlist as $procname) {
if (!$processors[$procname]->object->send_message($eventdata)) {
// Let new messaging class add custom content based on the processor.
$proceventdata = ($eventdata instanceof message) ? $eventdata->get_eventobject_for_processor($procname) : $eventdata;
if (!$processors[$procname]->object->send_message($proceventdata)) {
debugging('Error calling message processor ' . $procname);
$failed = true;
// Previously the $messageid = false here was overridden

View File

@ -0,0 +1,252 @@
<?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/>.
/**
* New messaging class.
*
* @package core_message
* @since Moodle 2.9
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\message;
defined('MOODLE_INTERNAL') || die();
/**
* New messaging class.
*
* Required parameters of the $eventdata object:
* component string Component name. must exist in message_providers
* name string Message type name. must exist in message_providers
* userfrom object|int The user sending the message
* userto object|int The message recipient
* subject string The message subject
* fullmessage string The full message in a given format
* fullmessageformat int The format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..)
* fullmessagehtml string The full version (the message processor will choose with one to use)
* smallmessage string The small version of the message
*
* Optional parameters of the $eventdata object:
* notification bool Should the message be considered as a notification rather than a personal message
* contexturl string If this is a notification then you can specify a url to view the event.
* For example the forum post the user is being notified of.
* contexturlname string The display text for contexturl.
* replyto string An email address which can be used to send an reply.
* attachment stored_file File instance that needs to be sent as attachment.
* attachname string Name of the attachment.
*
* @package core_message
* @since Moodle 2.9
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class message {
/** @var string Component name. */
private $component;
/** @var string Name. */
private $name;
/** @var object|int The user who is sending this message. */
private $userfrom;
/** @var object|int The user who is receiving from which is sending this message. */
private $userto;
/** @var string Subject of the message. */
private $subject;
/** @var string Complete message. */
private $fullmessage;
/** @var int Message format. */
private $fullmessageformat;
/** @var string Complete message in html format. */
private $fullmessagehtml;
/** @var string Smaller version of the message. */
private $smallmessage;
/** @var int Is it a notification? */
private $notification;
/** @var string context url. */
private $contexturl;
/** @var string context name. */
private $contexturlname;
/** @var string An email address which can be used to send an reply. */
private $replyto;
/** @var int Used internally to store the id of the row representing this message in DB. */
private $savedmessageid;
/** @var \stored_file File to be attached to the message. Note:- not all processors support this.*/
private $attachment;
/** @var string Name of the attachment. Note:- not all processors support this.*/
private $attachname;
/** @var array a list of properties that is allowed for each message. */
private $properties = array('component', 'name', 'userfrom', 'userto', 'subject', 'fullmessage', 'fullmessageformat',
'fullmessagehtml', 'smallmessage', 'notification', 'contexturl', 'contexturlname', 'savedmessageid',
'replyto', 'attachment', 'attachname');
/** @var array property to store any additional message processor specific content */
private $additionalcontent = array();
/**
* Fullmessagehtml content including any processor specific content.
*
* @param string $processorname Name of the processor.
*
* @return mixed|string
*/
protected function get_fullmessagehtml($processorname = '') {
if (!empty($processorname) && isset($this->additionalcontent[$processorname])) {
return $this->get_message_with_additional_content($processorname, 'fullmessagehtml');
} else {
return $this->fullmessagehtml;
}
}
/**
* Fullmessage content including any processor specific content.
*
* @param string $processorname Name of the processor.
*
* @return mixed|string
*/
protected function get_fullmessage($processorname = '') {
if (!empty($processorname) && isset($this->additionalcontent[$processorname])) {
return $this->get_message_with_additional_content($processorname, 'fullmessage');
} else {
return $this->fullmessage;
}
}
/**
* Smallmessage content including any processor specific content.
*
* @param string $processorname Name of the processor.
*
* @return mixed|string
*/
protected function get_smallmessage($processorname = '') {
if (!empty($processorname) && isset($this->additionalcontent[$processorname])) {
return $this->get_message_with_additional_content($processorname, 'smallmessage');
} else {
return $this->smallmessage;
}
}
/**
* Helper method used to get message content added with processor specific content.
*
* @param string $processorname Name of the processor.
* @param string $messagetype one of 'fullmessagehtml', 'fullmessage', 'smallmessage'.
*
* @return mixed|string
*/
protected function get_message_with_additional_content($processorname, $messagetype) {
$message = $this->$messagetype;
if (isset($this->additionalcontent[$processorname]['*'])) {
// Content that needs to be added to all format.
$pattern = $this->additionalcontent[$processorname]['*'];
$message = empty($pattern['header']) ? $message : $pattern['header'] . $message;
$message = empty($pattern['footer']) ? $message : $message . $pattern['footer'];
}
if (isset($this->additionalcontent[$processorname][$messagetype])) {
// Content that needs to be added to the specific given format.
$pattern = $this->additionalcontent[$processorname][$messagetype];
$message = empty($pattern['header']) ? $message : $pattern['header'] . $message;
$message = empty($pattern['footer']) ? $message : $message . $pattern['footer'];
}
return $message;
}
/**
* Magic getter method.
*
* @param string $prop name of property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception("Invalid property $prop specified");
}
/**
* Magic setter method.
*
* @param string $prop name of property to set.
* @param mixed $value value to assign to the property.
*
* @return mixed
* @throws \coding_exception
*/
public function __set($prop, $value) {
if (in_array($prop, $this->properties)) {
return $this->$prop = $value;
}
throw new \coding_exception("Invalid property $prop specified");
}
/**
* This method lets you define content that would be added to the message only for specific message processors.
*
* Example of $content:-
* array('fullmessagehtml' => array('header' => 'header content', 'footer' => 'footer content'),
* 'smallmessage' => array('header' => 'header content for small message', 'footer' => 'footer content'),
* '*' => array('header' => 'header content for all types', 'footer' => 'footer content')
* )
*
* @param string $processorname name of the processor.
* @param array $content content to add in the above defined format.
*/
public function set_additional_content($processorname, $content) {
$this->additionalcontent[$processorname] = $content;
}
/**
* Get a event object for a specific processor in stdClass format.
*
* @param string $processorname Name of the processor.
*
* @return \stdClass event object in stdClass format.
*/
public function get_eventobject_for_processor($processorname) {
// This is done for Backwards compatibility. We should consider throwing notices here in future versions and requesting
// them to use proper api.
$eventdata = new \stdClass();
foreach ($this->properties as $prop) {
$func = "get_$prop";
$eventdata->$prop = method_exists($this, $func) ? $this->$func($processorname) : $this->$prop;
}
return $eventdata;
}
}

View File

@ -51,7 +51,7 @@ require_once(dirname(dirname(__FILE__)) . '/message/lib.php');
* earlier versions did not do it consistently either.
*
* @category message
* @param stdClass $eventdata information about the message (component, userfrom, userto, ...)
* @param stdClass|\core\message\message $eventdata information about the message (component, userfrom, userto, ...)
* @return mixed the integer ID of the new message or false if there was a problem with submitted data
*/
function message_send($eventdata) {

204
lib/tests/message_test.php Normal file
View File

@ -0,0 +1,204 @@
<?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/>.
/**
* Test classes for \core\message\message.
*
* @package core_message
* @category test
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
/**
* Test script for message class.
*
* @package core_message
* @category test
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_message_testcase extends advanced_testcase {
/**
* Test the method get_eventobject_for_processor().
*/
public function test_get_eventobject_for_processor() {
global $USER;
$this->resetAfterTest();
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$message = new \core\message\message();
$message->component = 'moodle';
$message->name = 'instantmessage';
$message->userfrom = $USER;
$message->userto = $user;
$message->subject = 'message subject 1';
$message->fullmessage = 'message body';
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = '<p>message body</p>';
$message->smallmessage = 'small message';
$message->notification = '0';
$message->contexturl = 'http://GalaxyFarFarAway.com';
$message->contexturlname = 'Context name';
$message->replyto = "random@random.com";
$message->attachname = 'attachment';
$content = array('*' => array('header' => ' test ', 'footer' => ' test ')); // Extra content for all types of messages.
$message->set_additional_content('test', $content);
// Create a file instance.
$usercontext = context_user::instance($user->id);
$file = new stdClass;
$file->contextid = $usercontext->id;
$file->component = 'user';
$file->filearea = 'private';
$file->itemid = 0;
$file->filepath = '/';
$file->filename = '1.txt';
$file->source = 'test';
$fs = get_file_storage();
$file = $fs->create_file_from_string($file, 'file1 content');
$message->attachment = $file;
$stdclass = $message->get_eventobject_for_processor('test');
$this->assertSame($message->component, $stdclass->component);
$this->assertSame($message->name, $stdclass->name);
$this->assertSame($message->userfrom, $stdclass->userfrom);
$this->assertSame($message->userto, $stdclass->userto);
$this->assertSame($message->subject, $stdclass->subject);
$this->assertSame(' test ' . $message->fullmessage . ' test ', $stdclass->fullmessage);
$this->assertSame(' test ' . $message->fullmessagehtml . ' test ', $stdclass->fullmessagehtml);
$this->assertSame(' test ' . $message->smallmessage . ' test ', $stdclass->smallmessage);
$this->assertSame($message->notification, $stdclass->notification);
$this->assertSame($message->contexturl, $stdclass->contexturl);
$this->assertSame($message->contexturlname, $stdclass->contexturlname);
$this->assertSame($message->replyto, $stdclass->replyto);
$this->assertSame($message->attachname, $stdclass->attachname);
// Extra content for fullmessage only.
$content = array('fullmessage' => array('header' => ' test ', 'footer' => ' test '));
$message->set_additional_content('test', $content);
$stdclass = $message->get_eventobject_for_processor('test');
$this->assertSame(' test ' . $message->fullmessage . ' test ', $stdclass->fullmessage);
$this->assertSame($message->fullmessagehtml, $stdclass->fullmessagehtml);
$this->assertSame($message->smallmessage, $stdclass->smallmessage);
// Extra content for fullmessagehtml and smallmessage only.
$content = array('fullmessagehtml' => array('header' => ' test ', 'footer' => ' test '),
'smallmessage' => array('header' => ' testsmall ', 'footer' => ' testsmall '));
$message->set_additional_content('test', $content);
$stdclass = $message->get_eventobject_for_processor('test');
$this->assertSame($message->fullmessage, $stdclass->fullmessage);
$this->assertSame(' test ' . $message->fullmessagehtml . ' test ', $stdclass->fullmessagehtml);
$this->assertSame(' testsmall ' . $message->smallmessage . ' testsmall ', $stdclass->smallmessage);
// Extra content for * and smallmessage.
$content = array('*' => array('header' => ' test ', 'footer' => ' test '),
'smallmessage' => array('header' => ' testsmall ', 'footer' => ' testsmall '));
$message->set_additional_content('test', $content);
$stdclass = $message->get_eventobject_for_processor('test');
$this->assertSame(' test ' . $message->fullmessage . ' test ', $stdclass->fullmessage);
$this->assertSame(' test ' . $message->fullmessagehtml . ' test ', $stdclass->fullmessagehtml);
$this->assertSame(' testsmall ' . ' test ' . $message->smallmessage . ' test ' . ' testsmall ', $stdclass->smallmessage);
}
/**
* Test sending messages as email works with the new class.
*/
public function test_send_message() {
global $DB, $CFG;
$this->preventResetByRollback();
$this->resetAfterTest();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// Test basic email processor.
$this->assertFileExists("$CFG->dirroot/message/output/email/version.php");
$this->assertFileExists("$CFG->dirroot/message/output/popup/version.php");
$DB->set_field_select('message_processors', 'enabled', 0, "name <> 'email'");
set_user_preference('message_provider_moodle_instantmessage_loggedoff', 'email', $user2);
// Extra content for all types of messages.
$message = new \core\message\message();
$message->component = 'moodle';
$message->name = 'instantmessage';
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = 'message subject 1';
$message->fullmessage = 'message body';
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = '<p>message body</p>';
$message->smallmessage = 'small message';
$message->notification = '0';
$content = array('*' => array('header' => ' test ', 'footer' => ' test '));
$message->set_additional_content('email', $content);
$sink = $this->redirectEmails();
$messageid = message_send($message);
$emails = $sink->get_messages();
$this->assertCount(1, $emails);
$email = reset($emails);
$recordexists = $DB->record_exists('message_read', array('id' => $messageid));
$this->assertSame(true, $recordexists);
$this->assertSame($user1->email, $email->from);
$this->assertSame($user2->email, $email->to);
$this->assertSame($message->subject, $email->subject);
$this->assertNotEmpty($email->header);
$this->assertNotEmpty($email->body);
$this->assertRegExp('/test message body test/', $email->body);
$sink->clear();
// Extra content for small message only. Shouldn't show up in emails as we sent fullmessage and fullmessagehtml only in
// the emails.
$message = new \core\message\message();
$message->component = 'moodle';
$message->name = 'instantmessage';
$message->userfrom = $user1;
$message->userto = $user2;
$message->subject = 'message subject 1';
$message->fullmessage = 'message body';
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = '<p>message body</p>';
$message->smallmessage = 'small message';
$message->notification = '0';
$content = array('smallmessage' => array('header' => ' test ', 'footer' => ' test '));
$message->set_additional_content('email', $content);
$messageid = message_send($message);
$emails = $sink->get_messages();
$this->assertCount(1, $emails);
$email = reset($emails);
$recordexists = $DB->record_exists('message_read', array('id' => $messageid));
$this->assertSame(true, $recordexists);
$this->assertSame($user1->email, $email->from);
$this->assertSame($user2->email, $email->to);
$this->assertSame($message->subject, $email->subject);
$this->assertNotEmpty($email->header);
$this->assertNotEmpty($email->body);
$this->assertNotRegExp('/test message body test/', $email->body);
$sink->close();
}
}

View File

@ -775,7 +775,7 @@ function forum_cron() {
// Send the post now!
mtrace('Sending ', '');
$eventdata = new stdClass();
$eventdata = new \core\message\message();
$eventdata->component = 'mod_forum';
$eventdata->name = 'posts';
$eventdata->userfrom = $userfrom;
@ -786,6 +786,14 @@ function forum_cron() {
$eventdata->fullmessagehtml = $posthtml;
$eventdata->notification = 1;
$eventdata->replyto = $replyaddress;
if (!empty($replyaddress)) {
// Add extra text to email messages if they can reply back.
$textfooter = "\n\n" . get_string('replytopostbyemail', 'mod_forum');
$htmlfooter = html_writer::tag('p', get_string('replytopostbyemail', 'mod_forum'));
$additionalcontent = array('fullmessage' => array('footer' => $textfooter),
'fullmessagehtml' => array('footer' => $htmlfooter));
$eventdata->set_additional_content('email', $additionalcontent);
}
// If forum_replytouser is not set then send mail using the noreplyaddress.
if (empty($CFG->forum_replytouser)) {
@ -1238,10 +1246,6 @@ function forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfro
$posttext .= get_string("digestmailpost", "forum");
$posttext .= ": {$CFG->wwwroot}/mod/forum/index.php?id={$forum->course}\n";
if ($replyaddress) {
$posttext .= "\n\n" . get_string('replytopostbyemail', 'mod_forum');
}
return $posttext;
}
@ -1296,10 +1300,6 @@ function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfro
}
$posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
if ($replyaddress) {
$posthtml .= html_writer::tag('p', get_string('replytopostbyemail', 'mod_forum'));
}
$footerlinks = array();
if ($canunsubscribe) {
if (\mod_forum\subscriptions::is_subscribed($userto->id, $forum, null, $cm)) {