Merge branch 'MDL-64715-master-self-starred-pr-ryan' of git://github.com/sarjona...
authorAdrian Greeve <abgreeve@gmail.com>
Thu, 18 Apr 2019 03:06:57 +0000 (11:06 +0800)
committerAdrian Greeve <abgreeve@gmail.com>
Thu, 18 Apr 2019 03:06:57 +0000 (11:06 +0800)
1  2 
lib/db/upgrade.php
lib/upgrade.txt
message/amd/build/message_drawer_view_conversation_renderer.min.js
message/amd/build/message_drawer_view_overview.min.js
message/amd/build/message_drawer_view_overview_section.min.js
message/amd/src/message_drawer_view_conversation_renderer.js
message/amd/src/message_drawer_view_overview.js
message/amd/src/message_drawer_view_overview_section.js
version.php

@@@ -2989,16 -2989,188 +2989,200 @@@ function xmldb_main_upgrade($oldversion
      }
  
      if ($oldversion < 2019041300.01) {
 +        // Add the field 'name' to the 'analytics_models' table.
 +        $table = new xmldb_table('analytics_models');
 +        $field = new xmldb_field('name', XMLDB_TYPE_CHAR, '1333', null, null, null, null, 'trained');
 +
 +        if (!$dbman->field_exists($table, $field)) {
 +            $dbman->add_field($table, $field);
 +        }
 -        upgrade_main_savepoint(true, 2019041300.01);
++        // Main savepoint reached.
 +        upgrade_main_savepoint(true, 2019041300.01);
 +    }
 +
++    if ($oldversion < 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
+               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;
+             $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.
+         $select = 'useridfrom = useridto AND notification = 0';
+         $legacyselfmessagesrs = $DB->get_recordset_select('message_read', $select);
+         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;
+                 $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.id NOT IN (SELECT mcm.userid
+                                      FROM {message_conversation_members} mcm
+                                      INNER JOIN mdl_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);
+     }
      return true;
  }
diff --cc lib/upgrade.txt
@@@ -30,7 -30,14 +30,15 @@@ attribute on forms to avoid collisions 
    in this category. To work with list of courses use API methods in core_course_category and also 'course' form element.
  * 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).
  
  === 3.6 ===
  
@@@ -780,9 -800,11 +800,11 @@@ function
      var renderHeader = function(header, body, footer, data) {
          var headerContainer = getHeaderContent(header);
          var template = TEMPLATES.HEADER_PUBLIC;
 -
 +        data.context.showrouteback = (header.attr('data-from-panel') === "false");
          if (data.type == CONVERSATION_TYPES.PRIVATE) {
              template = data.showControls ? TEMPLATES.HEADER_PRIVATE : TEMPLATES.HEADER_PRIVATE_NO_CONTROLS;
+         } else if (data.type == CONVERSATION_TYPES.SELF) {
+             template = TEMPLATES.HEADER_SELF;
          }
  
          return Templates.render(template, data.context)
@@@ -236,17 -252,17 +253,17 @@@ function
  
          sections.forEach(function(args) {
              var sectionRoot = args[0];
-             var sectionType = args[1];
+             var sectionTypes = args[1];
              var includeFavourites = args[2];
              var totalCountPromise = allCounts.then(function(result) {
-                 return filterCountsByType(result.total, sectionType);
+                 return filterCountsByTypes(result.total, sectionTypes, includeFavourites);
              });
              var unreadCountPromise = allCounts.then(function(result) {
-                 return filterCountsByType(result.unread, sectionType);
+                 return filterCountsByTypes(result.unread, sectionTypes, includeFavourites);
              });
  
-             Section.show(namespace, null, sectionRoot, null, sectionType, includeFavourites,
+             Section.show(namespace, null, sectionRoot, null, sectionTypes, includeFavourites,
 -                totalCountPromise, unreadCountPromise);
 +                totalCountPromise, unreadCountPromise, fromPanel);
          });
  
          return allCounts.then(function(result) {
@@@ -523,12 -553,27 +553,28 @@@ function
       * @param {String} namespace Unique identifier for the Routes
       * @param {Object} root The section container element.
       * @param {Function} loadCallback The callback to load items.
-      * @param {Number} type The conversation type for this section
 -     * @param {Array|null} type The conversation types for this section
++     * @param {Array|null} types The conversation types for this section
       * @param {bool} includeFavourites If this section includes favourites
 +     * @param {String} fromPanel Routing argument to send if the section is loaded in message index left panel.
       */
-     var registerEventListeners = function(namespace, root, loadCallback, type, includeFavourites, fromPanel) {
 -    var registerEventListeners = function(namespace, root, loadCallback, types, includeFavourites) {
++    var registerEventListeners = function(namespace, root, loadCallback, types, includeFavourites, fromPanel) {
          var listRoot = LazyLoadList.getRoot(root);
+         var conversationBelongsToThisSection = function(conversation) {
+             // Make sure the type is an int so that the index of check matches correctly.
+             var conversationType = parseInt(conversation.type, 10);
+             if (
+                 // If the conversation type isn't one this section cares about then we can ignore it.
+                 (types && types.indexOf(conversationType) < 0) ||
+                 // If this is the favourites section and the conversation isn't a favourite then ignore it.
+                 (includeFavourites && !conversation.isFavourite) ||
+                 // If this section doesn't include favourites and the conversation is a favourite then ignore it.
+                 (!includeFavourites && conversation.isFavourite)
+             ) {
+                 return false;
+             }
+             return true;
+         };
  
          // Set the minimum height of the section to the height of the toggle. This
          // smooths out the collapse animation.
       * @param {bool} includeFavourites If this section includes favourites
       * @param {Object} totalCountPromise Resolves wth the total conversations count
       * @param {Object} unreadCountPromise Resolves wth the unread conversations count
 +     * @param {bool} fromPanel shown in message app panel.
       */
-     var show = function(namespace, header, body, footer, type, includeFavourites, totalCountPromise, unreadCountPromise,
 -    var show = function(namespace, header, body, footer, types, includeFavourites, totalCountPromise, unreadCountPromise) {
++    var show = function(namespace, header, body, footer, types, includeFavourites, totalCountPromise, unreadCountPromise,
 +        fromPanel) {
          var root = $(body);
  
          if (!root.attr('data-init')) {
-             var loadCallback = getLoadCallback(type, includeFavourites, 0);
-             registerEventListeners(namespace, root, loadCallback, type, includeFavourites, fromPanel);
+             var loadCallback = getLoadCallback(types, includeFavourites, 0);
 -            registerEventListeners(namespace, root, loadCallback, types, includeFavourites);
++            registerEventListeners(namespace, root, loadCallback, types, includeFavourites, fromPanel);
  
              if (isVisible(root)) {
                  setExpanded(root);
diff --cc version.php
@@@ -29,7 -29,7 +29,7 @@@
  
  defined('MOODLE_INTERNAL') || die();
  
- $version  = 2019041800.00;              // YYYYMMDD      = weekly release date of this DEV branch.
 -$version  = 2019041300.01;              // YYYYMMDD      = weekly release date of this DEV branch.
++$version  = 2019041800.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                          //         RR    = release increments - 00 in DEV branches.
                                          //           .XX = incremental changes.