예제 #1
0
/**
 * Called when a message provider wants to send a message.
 * This functions checks the message recipient's message processor configuration then
 * sends the message to the configured processors
 *
 * 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
 *
 * @category message
 * @param object $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 a processor
 */
function message_send($eventdata)
{
    global $CFG, $DB;
    //new message ID to return
    $messageid = false;
    // Fetch default (site) preferences
    $defaultpreferences = get_message_output_default_preferences();
    $preferencebase = $eventdata->component . '_' . $eventdata->name;
    // If message provider is disabled then don't do any processing.
    if (!empty($defaultpreferences->{$preferencebase . '_disable'})) {
        return $messageid;
    }
    //TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
    $DB->transactions_forbidden();
    // By default a message is a notification. Only personal/private messages aren't notifications.
    if (!isset($eventdata->notification)) {
        $eventdata->notification = 1;
    }
    if (is_number($eventdata->userto)) {
        $eventdata->userto = core_user::get_user($eventdata->userto);
    }
    if (is_int($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 = core_user::get_user($eventdata->userto->id);
    }
    //after how long inactive should the user be considered logged off?
    if (isset($CFG->block_online_users_timetosee)) {
        $timetoshowusers = $CFG->block_online_users_timetosee * 60;
    } else {
        $timetoshowusers = 300;
        //5 minutes
    }
    // Work out if the user is logged in or not
    if (!empty($eventdata->userto->lastaccess) && time() - $timetoshowusers < $eventdata->userto->lastaccess) {
        $userstate = 'loggedin';
    } else {
        $userstate = 'loggedoff';
    }
    // Create the message object
    $savemessage = new stdClass();
    $savemessage->useridfrom = $eventdata->userfrom->id;
    $savemessage->useridto = $eventdata->userto->id;
    $savemessage->subject = $eventdata->subject;
    $savemessage->fullmessage = $eventdata->fullmessage;
    $savemessage->fullmessageformat = $eventdata->fullmessageformat;
    $savemessage->fullmessagehtml = $eventdata->fullmessagehtml;
    $savemessage->smallmessage = $eventdata->smallmessage;
    $savemessage->notification = $eventdata->notification;
    if (!empty($eventdata->contexturl)) {
        $savemessage->contexturl = $eventdata->contexturl;
    } else {
        $savemessage->contexturl = null;
    }
    if (!empty($eventdata->contexturlname)) {
        $savemessage->contexturlname = $eventdata->contexturlname;
    } else {
        $savemessage->contexturlname = null;
    }
    $savemessage->timecreated = time();
    if (PHPUNIT_TEST and class_exists('phpunit_util')) {
        // Add some more tests to make sure the normal code can actually work.
        $componentdir = core_component::get_component_directory($eventdata->component);
        if (!$componentdir or !is_dir($componentdir)) {
            throw new coding_exception('Invalid component specified in message-send(): ' . $eventdata->component);
        }
        if (!file_exists("{$componentdir}/db/messages.php")) {
            throw new coding_exception("{$eventdata->component} does not contain db/messages.php necessary for message_send()");
        }
        $messageproviders = null;
        include "{$componentdir}/db/messages.php";
        if (!isset($messageproviders[$eventdata->name])) {
            throw new coding_exception("Missing messaging defaults for event '{$eventdata->name}' in '{$eventdata->component}' messages.php file");
        }
        unset($componentdir);
        unset($messageproviders);
        // Now ask phpunit if it wants to catch this message.
        if (phpunit_util::is_redirecting_messages()) {
            $savemessage->timeread = time();
            $messageid = $DB->insert_record('message_read', $savemessage);
            $message = $DB->get_record('message_read', array('id' => $messageid));
            phpunit_util::message_sent($message);
            return $messageid;
        }
    }
    // Fetch enabled processors
    $processors = get_message_processors(true);
    // Preset variables
    $processorlist = array();
    // 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})) {
            $permitted = $defaultpreferences->{$defaultpreference};
        } else {
            // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
            // exist in the message_provider table (thus there is no default settings for them).
            $preferrormsg = "Could not load preference {$defaultpreference}. Make sure the component and name you supplied\n                    to message_send() are valid.";
            throw new coding_exception($preferrormsg);
        }
        // Find out if user has configured this output
        // Some processors cannot function without settings from the user
        $userisconfigured = $processor->object->is_user_configured($eventdata->userto);
        // DEBUG: notify if we are forcing unconfigured output
        if ($permitted == 'forced' && !$userisconfigured) {
            debugging('Attempt to force message delivery to user who has "' . $processor->name . '" output unconfigured', DEBUG_NORMAL);
        }
        // Warn developers that necessary data is missing regardless of how the processors are configured
        if (!isset($eventdata->userto->emailstop)) {
            debugging('userto->emailstop is not set. Retrieving it from the user table');
            $eventdata->userto->emailstop = $DB->get_field('user', 'emailstop', array('id' => $eventdata->userto->id));
        }
        // Populate the list of processors we will be using
        if ($permitted == 'forced' && $userisconfigured) {
            // An admin is forcing users to use this message processor. Use this processor unconditionally.
            $processorlist[] = $processor->name;
        } else {
            if ($permitted == 'permitted' && $userisconfigured && !$eventdata->userto->emailstop) {
                // User has not disabled notifications
                // See if user set any notification preferences, otherwise use site default ones
                $userpreferencename = 'message_provider_' . $preferencebase . '_' . $userstate;
                if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto->id)) {
                    if (in_array($processor->name, explode(',', $userpreference))) {
                        $processorlist[] = $processor->name;
                    }
                } else {
                    if (isset($defaultpreferences->{$userpreferencename})) {
                        if (in_array($processor->name, explode(',', $defaultpreferences->{$userpreferencename}))) {
                            $processorlist[] = $processor->name;
                        }
                    }
                }
            }
        }
    }
    if (empty($processorlist) && $savemessage->notification) {
        //if they have deselected all processors and its a notification mark it read. The user doesnt want to be bothered
        $savemessage->timeread = time();
        $messageid = $DB->insert_record('message_read', $savemessage);
    } else {
        // Process the message
        // Store unread message just in case we can not send it
        $messageid = $savemessage->id = $DB->insert_record('message', $savemessage);
        $eventdata->savedmessageid = $savemessage->id;
        // Try to deliver the message to each processor
        if (!empty($processorlist)) {
            foreach ($processorlist as $procname) {
                if (!$processors[$procname]->object->send_message($eventdata)) {
                    debugging('Error calling message processor ' . $procname);
                    $messageid = false;
                }
            }
            //if messaging is disabled and they previously had forum notifications handled by the popup processor
            //or any processor that puts a row in message_working then the notification will remain forever
            //unread. To prevent this mark the message read if messaging is disabled
            if (empty($CFG->messaging)) {
                require_once $CFG->dirroot . '/message/lib.php';
                $messageid = message_mark_message_read($savemessage, time());
            } else {
                if ($DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0) {
                    //if there is no more processors that want to process this we can move message to message_read
                    require_once $CFG->dirroot . '/message/lib.php';
                    $messageid = message_mark_message_read($savemessage, time(), true);
                }
            }
        }
    }
    // We may be sending a message from the 'noreply' address, which means we are not actually sending a
    // message from a valid user. In this case, we will set the userid to 0.
    // Check if the userid is valid.
    if (core_user::is_real_user($eventdata->userfrom->id)) {
        $userfromid = $eventdata->userfrom->id;
    } else {
        $userfromid = 0;
    }
    // Trigger event for sending a message.
    $event = \core\event\message_sent::create(array('userid' => $userfromid, 'context' => context_system::instance(), 'relateduserid' => $eventdata->userto->id, 'other' => array('messageid' => $messageid)));
    $event->trigger();
    return $messageid;
}
예제 #2
0
 /**
  * Test the message sent event.
  *
  * We can not use the message_send() function in the unit test to check that the event was fired as there is a
  * conditional check to ensure a fake message is sent during unit tests when calling that particular function.
  */
 public function test_message_sent()
 {
     $event = \core\event\message_sent::create(array('userid' => 1, 'context' => context_system::instance(), 'relateduserid' => 2, 'other' => array('messageid' => 3)));
     // Trigger and capturing the event.
     $sink = $this->redirectEvents();
     $event->trigger();
     $events = $sink->get_events();
     $event = reset($events);
     // Check that the event data is valid.
     $this->assertInstanceOf('\\core\\event\\message_sent', $event);
     $this->assertEquals(context_system::instance(), $event->get_context());
     $expected = array(SITEID, 'message', 'write', 'index.php?user=1&id=2&history=1#m3', 1);
     $this->assertEventLegacyLogData($expected, $event);
     $url = new moodle_url('/message/index.php', array('user1' => $event->userid, 'user2' => $event->relateduserid));
     $this->assertEquals($url, $event->get_url());
 }
예제 #3
0
파일: manager.php 프로젝트: evltuma/moodle
 /**
  * Send message to message processors.
  *
  * @param \stdClass|\core\message\message $eventdata
  * @param \stdClass $savemessage
  * @param array $processorlist
  * @return int $messageid
  */
 protected static function send_message_to_processors($eventdata, \stdClass $savemessage, array $processorlist)
 {
     global $CFG, $DB;
     // We cannot communicate with external systems in DB transactions,
     // buffer the messages if necessary.
     if ($DB->is_transaction_started()) {
         // We need to clone all objects so that devs may not modify it from outside later.
         $eventdata = clone $eventdata;
         $eventdata->userto = clone $eventdata->userto;
         $eventdata->userfrom = clone $eventdata->userfrom;
         // Conserve some memory the same was as $USER setup does.
         unset($eventdata->userto->description);
         unset($eventdata->userfrom->description);
         self::$buffer[] = array($eventdata, $savemessage, $processorlist);
         return $savemessage->id;
     }
     $processors = get_message_processors(true);
     $failed = false;
     foreach ($processorlist as $procname) {
         // 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
             // by other processors and message_mark_message_read() below.
         }
     }
     // Trigger event for sending a message - must be done before marking as read.
     \core\event\message_sent::create_from_ids($eventdata->userfrom->id, $eventdata->userto->id, $savemessage->id)->trigger();
     if (empty($CFG->messaging)) {
         // If messaging is disabled and they previously had forum notifications handled by the popup processor
         // or any processor that puts a row in message_working then the notification will remain forever
         // unread. To prevent this mark the message read if messaging is disabled.
         $messageid = message_mark_message_read($savemessage, time());
     } else {
         if ($failed) {
             // Something failed, better keep it as unread then.
             $messageid = $savemessage->id;
         } else {
             if ($DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0) {
                 // If there is no more processors that want to process this we can move message to message_read.
                 $messageid = message_mark_message_read($savemessage, time(), true);
             } else {
                 // Some processor is still working on the data, let's keep it unread.
                 $messageid = $savemessage->id;
             }
         }
     }
     return $messageid;
 }
예제 #4
0
 public function test_mesage_sent_via_create_from_ids_without_other_courseid()
 {
     // Creating a message_sent event without courseid leads to debugging + SITEID.
     // TODO: MDL-55449 Ensure this leads to exception instead of debugging in Moodle 3.6.
     $event = \core\event\message_sent::create_from_ids(1, 2, 3);
     // Trigger and capturing the event.
     $sink = $this->redirectEvents();
     $event->trigger();
     $events = $sink->get_events();
     $event = reset($events);
     $this->assertDebuggingCalled();
     $this->assertEquals(SITEID, $event->other['courseid']);
 }