Skip to content

Commit

Permalink
MDL-31560 Messages: Added support for noreply and support users
Browse files Browse the repository at this point in the history
This is used by messaging system for sending/receiving message
to/from noreply or support user. message_send api will now use
core_user class to get noreply or support user and then
send/receive message depending on user state.
  • Loading branch information
Rajesh Taneja committed Sep 13, 2013
1 parent 83f26f6 commit 3bcf6b3
Show file tree
Hide file tree
Showing 11 changed files with 364 additions and 29 deletions.
8 changes: 8 additions & 0 deletions config-dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,14 @@
// config.php file
// $CFG->preventexecpath = true;
//
// Use the following flag to set userid for noreply user. If not set then moodle will
// create dummy user and use -ve value as user id.
// $CFG->noreplyuserid = -10;
//
// As of version 2.6 Moodle supports admin to set support user. If not set, all mails
// will be sent to supportemail.
// $CFG->supportuserid = -20;
//
//=========================================================================
// 7. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
//=========================================================================
Expand Down
21 changes: 4 additions & 17 deletions error/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,12 @@
redirect($CFG->wwwroot);
}

/// Work out who to send the message to
if (!$admin = get_admin() ) {
print_error('cannotfindadmin', 'debug');
}

$supportuser = new stdClass();
$supportuser->email = $CFG->supportemail ? $CFG->supportemail : $admin->email;
$supportuser->firstname = $CFG->supportname ? $CFG->supportname : $admin->firstname;
$supportuser->lastname = $CFG->supportname ? '' : $admin->lastname;
// emailstop could be hard coded "false" to ensure error reports are sent
// but then admin's would have to alter their messaging preferences to temporarily stop them
$supportuser->emailstop = $admin->emailstop;
$supportuser->maildisplay = true;

/// Send the message and redirect
// Send the message and redirect.
$eventdata = new stdClass();
$eventdata->modulename = 'moodle';
$eventdata->component = 'moodle';
$eventdata->name = 'errors';
$eventdata->userfrom = $USER;
$eventdata->userto = $supportuser;
$eventdata->userto = core_user::get_support_user();
$eventdata->subject = 'Error: '. $form->referer .' -> '. $form->requested;
$eventdata->fullmessage = $form->text;
$eventdata->fullmessageformat = FORMAT_PLAIN;
Expand Down
211 changes: 211 additions & 0 deletions lib/classes/user.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<?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/>.

/**
* User class
*
* @package core
* @copyright 2013 Rajesh Taneja <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

/**
* User class to access user details.
*
* @todo move api's from user/lib.php and depreciate old ones.
* @package core
* @copyright 2013 Rajesh Taneja <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_user {
/**
* No reply user id.
*/
const NOREPLY_USER = -10;

/**
* Suppport user id.
*/
const SUPPORT_USER = -20;

/** @var stdClass keep record of noreply user */
public static $noreplyuser = false;

/** @var stdClass keep record of support user */
public static $supportuser = false;

/**
* Return user object from db or create noreply or support user,
* if userid matches corse_user::NOREPLY_USER or corse_user::SUPPORT_USER
* respectively. If userid is not found, then return false.
*
* @param int $userid user id
* @param string $fields A comma separated list of user fields to be returned, support and noreply user
* will not be filtered by this.
* @param int $strictness IGNORE_MISSING means compatible mode, false returned if user not found, debug message if more found;
* IGNORE_MULTIPLE means return first user, ignore multiple user records found(not recommended);
* MUST_EXIST means throw an exception if no user record or multiple records found.
* @return stdClass|bool user record if found, else false.
* @throws dml_exception if user record not found and respective $strictness is set.
*/
public static function get_user($userid, $fields = '*', $strictness = IGNORE_MISSING) {
global $DB;

// If noreply user then create fake record and return.
switch ($userid) {
case self::NOREPLY_USER:
return self::get_noreply_user($strictness);
break;
case self::SUPPORT_USER:
return self::get_support_user($strictness);
break;
default:
return $DB->get_record('user', array('id' => $userid), $fields, $strictness);
}
}

/**
* Helper function to return dummy noreply user record.
*
* @return stdClass
*/
protected static function get_dummy_user_record() {
global $CFG;

$dummyuser = new stdClass();
$dummyuser->id = self::NOREPLY_USER;
$dummyuser->email = $CFG->noreplyaddress;
$dummyuser->firstname = get_string('noreplyname');
$dummyuser->username = 'noreply';
$dummyuser->lastname = '';
$dummyuser->confirmed = 1;
$dummyuser->suspended = 0;
$dummyuser->deleted = 0;
$dummyuser->picture = 0;
$dummyuser->auth = 'manual';
$dummyuser->firstnamephonetic = '';
$dummyuser->lastnamephonetic = '';
$dummyuser->middlename = '';
$dummyuser->alternatename = '';
$dummyuser->imagealt = '';
return $dummyuser;
}

/**
* Return noreply user record, this is currently used in messaging
* system only for sending messages from noreply email.
* It will return record of $CFG->noreplyuserid if set else return dummy
* user object with hard-coded $user->emailstop = 1 so noreply can be sent to user.
*
* @return stdClass user record.
*/
public static function get_noreply_user() {
global $CFG;

if (!empty(self::$noreplyuser)) {
return self::$noreplyuser;
}

// If noreply user is set then use it, else create one.
if (!empty($CFG->noreplyuserid)) {
self::$noreplyuser = self::get_user($CFG->noreplyuserid);
}

if (empty(self::$noreplyuser)) {
self::$noreplyuser = self::get_dummy_user_record();
}
self::$noreplyuser->emailstop = 1; // Force msg stop for this user.
return self::$noreplyuser;
}

/**
* Return support user record, this is currently used in messaging
* system only for sending messages to support email.
* $CFG->supportuserid is set then returns user record
* $CFG->supportemail is set then return dummy record with $CFG->supportemail
* else return admin user record with hard-coded $user->emailstop = 0, so user
* gets support message.
*
* @return stdClass user record.
*/
public static function get_support_user() {
global $CFG;

if (!empty(self::$supportuser)) {
return self::$supportuser;
}

// If custom support user is set then use it, else if supportemail is set then use it, else use noreply.
if (!empty($CFG->supportuserid)) {
self::$supportuser = self::get_user($CFG->supportuserid, '*', MUST_EXIST);
}

// Try sending it to support email if support user is not set.
if (empty(self::$supportuser) && !empty($CFG->supportemail)) {
self::$supportuser = self::get_dummy_user_record();
self::$supportuser->id = self::SUPPORT_USER;
self::$supportuser->email = $CFG->supportemail;
self::$supportuser->firstname = $CFG->supportname ? $CFG->supportname : $supportuser->firstname;
self::$supportuser->username = 'support';
self::$supportuser->maildisplay = true;
}

// Send support msg to admin user if nothing is set above.
if (empty(self::$supportuser)) {
self::$supportuser = get_admin();
}

// Unset emailstop to make sure support message is sent.
self::$supportuser->emailstop = 0;
return self::$supportuser;
}

/**
* Reset self::$noreplyuser and self::$supportuser.
* This is only used by phpunit, and there is no other use case for this function.
* Please don't use it outside phpunit.
*/
public static function reset_internal_users() {
if (PHPUNIT_TEST) {
self::$noreplyuser = false;
self::$supportuser = false;
} else {
debugging('reset_internal_users() should not be used outside phpunit.', DEBUG_DEVELOPER);
}
}

/**
* Return true is user id is greater than self::NOREPLY_USER and
* alternetely check db.
*
* @param int $userid user id.
* @param bool $checkdb if true userid will be checked in db. By default it's false, and
* userid is compared with NOREPLY_USER for performance.
* @return bool true is real user else false.
*/
public static function is_real_user($userid, $checkdb = false) {
if ($userid < 0) {
return false;
}
if ($checkdb) {
return $DB->record_exists('user', array('id' => $userid));
} else {
return true;
}
}
}
19 changes: 16 additions & 3 deletions lib/messagelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,21 @@ function message_send($eventdata) {
$DB->transactions_forbidden();

if (is_number($eventdata->userto)) {
$eventdata->userto = $DB->get_record('user', array('id' => $eventdata->userto));
$eventdata->userto = core_user::get_user($eventdata->userto);
}
if (is_int($eventdata->userfrom)) {
$eventdata->userfrom = $DB->get_record('user', array('id' => $eventdata->userfrom));
$eventdata->userfrom = core_user::get_user($eventdata->userfrom);
}

$usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false);
// If recipient is internal user (noreply user), and emailstop is set then don't send any msg.
if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) {
debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL);
return false;
}

if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended) or !isset($eventdata->userto->deleted)) {
$eventdata->userto = $DB->get_record('user', array('id' => $eventdata->userto->id));
$eventdata->userto = core_user::get_user($eventdata->userto->id);
}

//after how long inactive should the user be considered logged off?
Expand Down Expand Up @@ -150,6 +158,11 @@ function message_send($eventdata) {
$preferencebase = $eventdata->component.'_'.$eventdata->name;
// Fill in the array of processors to be used based on default and user preferences
foreach ($processors as $processor) {
// Skip adding processors for internal user, if processor doesn't support sending message to internal user.
if (!$usertoisrealuser && !$processor->object->can_send_to_any_users()) {
continue;
}

// First find out permissions
$defaultpreference = $processor->name.'_provider_'.$preferencebase.'_permitted';
if (isset($defaultpreferences->{$defaultpreference})) {
Expand Down
81 changes: 81 additions & 0 deletions lib/tests/user_test.php
Original file line number Diff line number Diff line change
@@ -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/>.

/**
* Tests core_user class.
*
* @package core
* @copyright 2013 Rajesh Taneja <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

/**
* Test core_user class.
*
* @package core
* @copyright 2013 Rajesh Taneja <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_user_testcase extends advanced_testcase {

public function test_get_user() {
global $CFG;

$this->resetAfterTest(true);

// Create user and try fetach it with api.
$user = $this->getDataGenerator()->create_user();
$this->assertEquals($user, core_user::get_user($user->id, '*', MUST_EXIST));

// Test noreply user.
$CFG->noreplyuserid = null;
$noreplyuser = core_user::get_noreply_user();
$this->assertEquals(1, $noreplyuser->emailstop);
$this->assertFalse(core_user::is_real_user($noreplyuser->id));
$this->assertEquals($CFG->noreplyaddress, $noreplyuser->email);
$this->assertEquals(get_string('noreplyname'), $noreplyuser->firstname);

// Set user as noreply user and make sure noreply propery is set.
core_user::reset_internal_users();
$CFG->noreplyuserid = $user->id;
$noreplyuser = core_user::get_noreply_user();
$this->assertEquals(1, $noreplyuser->emailstop);
$this->assertTrue(core_user::is_real_user($noreplyuser->id));

// Test support user.
core_user::reset_internal_users();
$CFG->supportemail = null;
$CFG->noreplyuserid = null;
$supportuser = core_user::get_support_user();
$adminuser = get_admin();
$this->assertEquals($adminuser, $supportuser);
$this->assertTrue(core_user::is_real_user($supportuser->id));

// When supportemail is set.
core_user::reset_internal_users();
$CFG->supportemail = '[email protected]';
$supportuser = core_user::get_support_user();
$this->assertEquals(core_user::SUPPORT_USER, $supportuser->id);
$this->assertFalse(core_user::is_real_user($supportuser->id));

// Set user as support user and make sure noreply propery is set.
core_user::reset_internal_users();
$CFG->supportuserid = $user->id;
$supportuser = core_user::get_support_user();
$this->assertEquals($user, $supportuser);
$this->assertTrue(core_user::is_real_user($supportuser->id));
}
}
4 changes: 4 additions & 0 deletions lib/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ information provided here is intended especially for developers.
get_fast_modinfo(). Purging all caches and every core upgrade purges course modinfo cache as well.
If function get_fast_modinfo() is called for multiple courses make sure to include field cacherev in course
object.
* Internal (noreply and support) user support has been added for sending/receiving message.
Use core_user::get_noreply_user() and core_user::get_support_user() to get noreply and support user's respectively.
Real users can be used as noreply/support users by setting $CFG->noreplyuserid and $CFG->supportuserid

DEPRECATIONS:
Various previously deprecated functions have now been altered to throw DEBUG_DEVELOPER debugging notices
Expand Down Expand Up @@ -104,6 +107,7 @@ Misc:
* js_minify() -> core_minify::js_files()
* css_minify_css() -> core_minify::css_files()
* course_modinfo::build_section_cache() -> (no replacement)
* generate_email_supportuser() -> core_user::get_support_user()

User-agent related functions:
* check_browser_operating_system() -> core_useragent::check_browser_operating_system()
Expand Down
Loading

0 comments on commit 3bcf6b3

Please sign in to comment.