Merge branch 'MDL-60680-master' of git://github.com/jleyva/moodle
authorDavid Monllaó <davidm@moodle.com>
Thu, 25 Apr 2019 16:54:19 +0000 (18:54 +0200)
committerDavid Monllaó <davidm@moodle.com>
Thu, 25 Apr 2019 16:55:45 +0000 (18:55 +0200)
17 files changed:
1  2 
lang/en/message.php
lang/en/moodle.php
lib/classes/message/manager.php
lib/classes/message/message.php
lib/db/install.xml
lib/db/upgrade.php
lib/messagelib.php
lib/outputrenderers.php
lib/tests/messagelib_test.php
lib/upgrade.txt
message/classes/api.php
message/classes/privacy/provider.php
message/externallib.php
message/tests/api_test.php
message/tests/externallib_test.php
message/upgrade.txt
version.php

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -3001,244 -3001,33 +3001,274 @@@ function xmldb_main_upgrade($oldversion
      }
  
      if ($oldversion < 2019041800.01) {
 -        upgrade_main_savepoint(true, 2019041800.01);
 +        // STEP 1. For the existing and migrated self-conversations, set the type to the new MESSAGE_CONVERSATION_TYPE_SELF, update
 +        // the convhash and star them.
 +        $sql = "SELECT mcm.conversationid, mcm.userid, MAX(mcm.id) as maxid
 +                  FROM {message_conversation_members} mcm
 +            INNER JOIN {user} u ON mcm.userid = u.id
 +                 WHERE u.deleted = 0
 +              GROUP BY mcm.conversationid, mcm.userid
 +                HAVING COUNT(*) > 1";
 +        $selfconversationsrs = $DB->get_recordset_sql($sql);
 +        $maxids = [];
 +        foreach ($selfconversationsrs as $selfconversation) {
 +            $DB->update_record('message_conversations',
 +                ['id' => $selfconversation->conversationid,
 +                 'type' => \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
 +                 'convhash' => \core_message\helper::get_conversation_hash([$selfconversation->userid])
 +                ]
 +            );
 +
 +            // Star the existing self-conversation.
 +            $favouriterecord = new \stdClass();
 +            $favouriterecord->component = 'core_message';
 +            $favouriterecord->itemtype = 'message_conversations';
 +            $favouriterecord->itemid = $selfconversation->conversationid;
 +            $userctx = \context_user::instance($selfconversation->userid);
 +            $favouriterecord->contextid = $userctx->id;
 +            $favouriterecord->userid = $selfconversation->userid;
 +            if (!$DB->record_exists('favourite', (array)$favouriterecord)) {
 +                $favouriterecord->timecreated = time();
 +                $favouriterecord->timemodified = $favouriterecord->timecreated;
 +                $DB->insert_record('favourite', $favouriterecord);
 +            }
 +
 +            // Set the self-conversation member with maxid to remove it later.
 +            $maxids[] = $selfconversation->maxid;
 +        }
 +        $selfconversationsrs->close();
 +
 +        // Remove the repeated member with the higher id for all the existing self-conversations.
 +        if (!empty($maxids)) {
 +            list($insql, $inparams) = $DB->get_in_or_equal($maxids);
 +            $DB->delete_records_select('message_conversation_members', "id $insql", $inparams);
 +        }
 +
 +        // STEP 2. Migrate existing self-conversation relying on old message tables, setting the type to the new
 +        // MESSAGE_CONVERSATION_TYPE_SELF and the convhash to the proper one. Star them also.
 +
 +        // On the messaging legacy tables, self-conversations are only present in the 'message_read' table, so we don't need to
 +        // check the content in the 'message' table.
 +        $sql = "SELECT mr.*
 +                  FROM {message_read} mr
 +            INNER JOIN {user} u ON mr.useridfrom = u.id
 +                 WHERE mr.useridfrom = mr.useridto AND mr.notification = 0 AND u.deleted = 0";
 +        $legacyselfmessagesrs = $DB->get_recordset_sql($sql);
 +        foreach ($legacyselfmessagesrs as $message) {
 +            // Get the self-conversation or create and star it if doesn't exist.
 +            $conditions = [
 +                'type' => \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
 +                'convhash' => \core_message\helper::get_conversation_hash([$message->useridfrom])
 +            ];
 +            $selfconversation = $DB->get_record('message_conversations', $conditions);
 +            if (empty($selfconversation)) {
 +                // Create the self-conversation.
 +                $selfconversation = new \stdClass();
 +                $selfconversation->type = \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF;
 +                $selfconversation->convhash = \core_message\helper::get_conversation_hash([$message->useridfrom]);
 +                $selfconversation->enabled = 1;
 +                $selfconversation->timecreated = time();
 +                $selfconversation->timemodified = $selfconversation->timecreated;
 +
 +                $selfconversation->id = $DB->insert_record('message_conversations', $selfconversation);
 +
 +                // Add user to this self-conversation.
 +                $member = new \stdClass();
 +                $member->conversationid = $selfconversation->id;
 +                $member->userid = $message->useridfrom;
 +                $member->timecreated = time();
 +
 +                $member->id = $DB->insert_record('message_conversation_members', $member);
 +
 +                // Star the self-conversation.
 +                $favouriterecord = new \stdClass();
 +                $favouriterecord->component = 'core_message';
 +                $favouriterecord->itemtype = 'message_conversations';
 +                $favouriterecord->itemid = $selfconversation->id;
 +                $userctx = \context_user::instance($message->useridfrom);
 +                $favouriterecord->contextid = $userctx->id;
 +                $favouriterecord->userid = $message->useridfrom;
 +                if (!$DB->record_exists('favourite', (array)$favouriterecord)) {
 +                    $favouriterecord->timecreated = time();
 +                    $favouriterecord->timemodified = $favouriterecord->timecreated;
 +                    $DB->insert_record('favourite', $favouriterecord);
 +                }
 +            }
 +
 +            // Create the object we will be inserting into the database.
 +            $tabledata = new \stdClass();
 +            $tabledata->useridfrom = $message->useridfrom;
 +            $tabledata->conversationid = $selfconversation->id;
 +            $tabledata->subject = $message->subject;
 +            $tabledata->fullmessage = $message->fullmessage;
 +            $tabledata->fullmessageformat = $message->fullmessageformat ?? FORMAT_MOODLE;
 +            $tabledata->fullmessagehtml = $message->fullmessagehtml;
 +            $tabledata->smallmessage = $message->smallmessage;
 +            $tabledata->timecreated = $message->timecreated;
 +
 +            $messageid = $DB->insert_record('messages', $tabledata);
 +
 +            // Check if we need to mark this message as deleted (self-conversations add this information on the
 +            // timeuserfromdeleted field.
 +            if ($message->timeuserfromdeleted) {
 +                $mua = new \stdClass();
 +                $mua->userid = $message->useridfrom;
 +                $mua->messageid = $messageid;
 +                $mua->action = \core_message\api::MESSAGE_ACTION_DELETED;
 +                $mua->timecreated = $message->timeuserfromdeleted;
 +
 +                $DB->insert_record('message_user_actions', $mua);
 +            }
 +
 +            // Mark this message as read.
 +            $mua = new \stdClass();
 +            $mua->userid = $message->useridto;
 +            $mua->messageid = $messageid;
 +            $mua->action = \core_message\api::MESSAGE_ACTION_READ;
 +            $mua->timecreated = $message->timeread;
 +
 +            $DB->insert_record('message_user_actions', $mua);
 +        }
 +        $legacyselfmessagesrs->close();
 +
 +        // We can now delete the records from legacy table because the self-conversations have been migrated from the legacy tables.
 +        $DB->delete_records_select('message_read', $select);
 +
 +        // STEP 3. For existing users without self-conversations, create and star it.
 +
 +        // Get all the users without a self-conversation.
 +        $sql = "SELECT u.id
 +                  FROM {user} u
 +                  WHERE u.deleted = 0 AND u.id NOT IN (SELECT mcm.userid
 +                                     FROM {message_conversation_members} mcm
 +                                     INNER JOIN {message_conversations} mc
 +                                             ON mc.id = mcm.conversationid AND mc.type = ?
 +                                    )";
 +        $useridsrs = $DB->get_recordset_sql($sql, [\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]);
 +        // Create the self-conversation for all these users.
 +        foreach ($useridsrs as $user) {
 +            $conditions = [
 +                'type' => \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
 +                'convhash' => \core_message\helper::get_conversation_hash([$user->id])
 +            ];
 +            $selfconversation = $DB->get_record('message_conversations', $conditions);
 +            if (empty($selfconversation)) {
 +                // Create the self-conversation.
 +                $selfconversation = new \stdClass();
 +                $selfconversation->type = \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF;
 +                $selfconversation->convhash = \core_message\helper::get_conversation_hash([$user->id]);
 +                $selfconversation->enabled = 1;
 +                $selfconversation->timecreated = time();
 +                $selfconversation->timemodified = $selfconversation->timecreated;
 +
 +                $selfconversation->id = $DB->insert_record('message_conversations', $selfconversation);
 +
 +                // Add user to this self-conversation.
 +                $member = new \stdClass();
 +                $member->conversationid = $selfconversation->id;
 +                $member->userid = $user->id;
 +                $member->timecreated = time();
 +
 +                $member->id = $DB->insert_record('message_conversation_members', $member);
 +
 +                // Star the self-conversation.
 +                $favouriterecord = new \stdClass();
 +                $favouriterecord->component = 'core_message';
 +                $favouriterecord->itemtype = 'message_conversations';
 +                $favouriterecord->itemid = $selfconversation->id;
 +                $userctx = \context_user::instance($user->id);
 +                $favouriterecord->contextid = $userctx->id;
 +                $favouriterecord->userid = $user->id;
 +                $favouriterecord->timecreated = time();
 +                $favouriterecord->timemodified = $favouriterecord->timecreated;
 +
 +                $DB->insert_record('favourite', $favouriterecord);
 +            }
 +        }
 +        $useridsrs->close();
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2019041800.01);
 +    }
 +
 +    if ($oldversion < 2019042200.01) {
 +
 +        // Define table role_sortorder to be dropped.
 +        $table = new xmldb_table('role_sortorder');
 +
 +        // Conditionally launch drop table for role_sortorder.
 +        if ($dbman->table_exists($table)) {
 +            $dbman->drop_table($table);
 +        }
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2019042200.01);
 +    }
 +
 +    if ($oldversion < 2019042200.02) {
 +
 +        // Let's update all (old core) targets to their new (core_course) locations.
 +        $targets = [
 +            '\core\analytics\target\course_competencies' => '\core_course\analytics\target\course_competencies',
 +            '\core\analytics\target\course_completion' => '\core_course\analytics\target\course_completion',
 +            '\core\analytics\target\course_dropout' => '\core_course\analytics\target\course_dropout',
 +            '\core\analytics\target\course_gradetopass' => '\core_course\analytics\target\course_gradetopass',
 +            '\core\analytics\target\no_teaching' => '\core_course\analytics\target\no_teaching',
 +        ];
 +
 +        foreach ($targets as $oldclass => $newclass) {
 +            $DB->set_field('analytics_models', 'target', $newclass, ['target' => $oldclass]);
 +        }
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2019042200.02);
 +    }
 +
 +    if ($oldversion < 2019042300.01) {
 +        $sql = "UPDATE {capabilities}
 +                   SET name = ?,
 +                       contextlevel = ?
 +                 WHERE name = ?";
 +        $DB->execute($sql, ['moodle/category:viewcourselist', CONTEXT_COURSECAT, 'moodle/course:browse']);
 +
 +        $sql = "UPDATE {role_capabilities}
 +                   SET capability = ?
 +                 WHERE capability = ?";
 +        $DB->execute($sql, ['moodle/category:viewcourselist', 'moodle/course:browse']);
 +
 +        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2019042300.01);
 +    }
 +
++    if ($oldversion < 2019042300.03) {
++
+         // Add new customdata field to message table.
+         $table = new xmldb_table('message');
+         $field = new xmldb_field('customdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'eventtype');
+         // Conditionally launch add field output.
+         if (!$dbman->field_exists($table, $field)) {
+             $dbman->add_field($table, $field);
+         }
+         // Add new customdata field to notifications and messages table.
+         $table = new xmldb_table('notifications');
+         $field = new xmldb_field('customdata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'timecreated');
+         // Conditionally launch add field output.
+         if (!$dbman->field_exists($table, $field)) {
+             $dbman->add_field($table, $field);
+         }
+         $table = new xmldb_table('messages');
+         // Conditionally launch add field output.
+         if (!$dbman->field_exists($table, $field)) {
+             $dbman->add_field($table, $field);
+         }
+         // Main savepoint reached.
++        upgrade_main_savepoint(true, 2019042300.03);
+     }
      return true;
  }
Simple merge
Simple merge
Simple merge
diff --cc lib/upgrade.txt
@@@ -30,18 -31,12 +30,24 @@@ attribute on forms to avoid collisions 
  * It is possible to pass additional conditions to get_courses_search();
    core_course_category::search_courses() now allows to search only among courses with completion enabled.
  * Add support for a new xxx_after_require_login callback
 +* A new conversation type has been created for self-conversations. During the upgrading process:
 +  - Firstly, the existing self-conversations will be starred and migrated to the new type, removing the duplicated members in the
 +  message_conversation_members table.
 +  - Secondly, the legacy self conversations will be migrated from the legacy 'message_read' table. They will be created using the
 +  new conversation type and will be favourited.
 +  - Finally, the self-conversations for all remaining users without them will be created and starred.
 +Besides, from now, a self-conversation will be created and starred by default to all the new users (even when $CFG->messaging
 +is disabled).
 +* New optional parameter $throwexception for \get_complete_user_data(). If true, an exception will be thrown when there's no
 +  matching record found or when there are multiple records found for the given field value. If false, it will simply return false.
 +  Defaults to false when not set.
 +* Exposed submit button to allow custom styling (via customclassoverride variable) which can override btn-primary/btn-secondary classes
+ * `$includetoken` parameter type has been changed. Now supports:
+    boolean: False indicates to not include the token, true indicates to generate a token for the current user ($USER).
+    integer: Indicates to generate a token for the user whose id is the integer value.
+ * The following functions have been updated to support the new usage:
+     - make_pluginfile_url
+     - file_rewrite_pluginfile_urls
  
  === 3.6 ===
  
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -9,9 -9,13 +9,16 @@@ information provided here is intended e
    functionality and remove all the legacy code (see MDL-63915).
    Note - It's still possible to view another user's messages if you have the right capabilities and are able to
    'log-in as' them.
 +* A new parameter 'mergeself' has been added to the methods \core_message\api::get_conversations() and
 +  core_message_external::get_conversations(), to decide whether the self-conversations should be included or not when the
 +  private ones are requested, to display them together.
+ * A new 'customdata' field for both messages and notifications has been added. This new field can store any custom data
+   serialised using json_encode().
+   This new field can be used for storing any data not fitting in the current message structure. For example, it will be used
+   to store additional information for the "Mobile notifications" processor.
+   Existing external functions: core_message_get_messages and message_popup_get_popup_notifications has been udated to return the
+   new field.
+ * External function core_message_get_messages now returns the component and eventtype.
  
  === 3.6 ===
  
diff --cc version.php
@@@ -29,7 -29,7 +29,7 @@@
  
  defined('MOODLE_INTERNAL') || die();
  
- $version  = 2019042300.01;              // YYYYMMDD      = weekly release date of this DEV branch.
 -$version  = 2019041800.01;              // YYYYMMDD      = weekly release date of this DEV branch.
++$version  = 2019042300.03;              // YYYYMMDD      = weekly release date of this DEV branch.
                                          //         RR    = release increments - 00 in DEV branches.
                                          //           .XX = incremental changes.