Merge branch 'MDL-65191-master' of git://github.com/lameze/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 8 Apr 2019 22:46:47 +0000 (00:46 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 8 Apr 2019 22:46:47 +0000 (00:46 +0200)
1  2 
lang/en/admin.php
lib/badgeslib.php
lib/db/tasks.php
version.php

diff --combined lang/en/admin.php
@@@ -47,6 -47,7 +47,6 @@@ $string['allcountrycodes'] = 'All count
  $string['allowattachments'] = 'Allow attachments';
  $string['allowbeforeblock'] = 'Allowed list will be processed first';
  $string['allowbeforeblockdesc'] = 'By default, entries in the blocked IPs list are matched first. If this option is enabled, entries in the allowed IPs list are processed before the blocked list.';
 -$string['allowblockstodock'] = 'Allow blocks to use the dock';
  $string['allowcategorythemes'] = 'Allow category themes';
  $string['allowcohortthemes'] = 'Allow cohort themes';
  $string['allowcoursethemes'] = 'Allow course themes';
@@@ -145,6 -146,7 +145,6 @@@ $string['configallowattachments'] = 'I
  $string['configenableactivitychooser'] = 'The activity chooser is a dialog box with a short description of each activity and resource. If disabled, separate resource and activity drop-down menus are provided instead.';
  $string['configallcountrycodes'] = 'This is the list of countries that may be selected in various places, for example in a user\'s profile. If blank (the default) the list in countries.php in the standard English language pack is used. That is the list from ISO 3166-1. Otherwise, you can specify a comma-separated list of codes, for example \'GB,FR,ES\'. If you add new, non-standard codes here, you will need to add them to countries.php in \'en\' and your language pack.';
  $string['configallowassign'] = 'You can allow people who have the roles on the left side to assign some of the column roles to other people';
 -$string['configallowblockstodock'] = 'If enabled and supported by the selected theme users can choose to move blocks to a special dock.';
  $string['configallowcategorythemes'] = 'If you enable this, then themes can be set at the category level. This will affect all child categories and courses unless they have specifically set their own theme. WARNING: Enabling category themes may affect performance.';
  $string['configallowcohortthemes'] = 'If you enable this, then themes can be set at the cohort level. This will affect all users with only one cohort or more than one but with the same theme.';
  $string['configallowcoursethemes'] = 'If you enable this, then courses will be allowed to set their own themes.  Course themes override all other theme choices (site, user, or session themes)';
@@@ -221,7 -223,6 +221,7 @@@ $string['configeditordictionary'] = 'Th
  $string['configeditorfontlist'] = 'Select the fonts that should appear in the editor\'s drop-down list.';
  $string['configemailchangeconfirmation'] = 'Require an email confirmation step when users change their email address in their profile.';
  $string['configemailfromvia'] = 'Add via information in the "From" section of outgoing email. This informs the recipient from where this email came from and also helps combat recipients accidentally replying to no-reply email addresses.';
 +$string['configemailsubjectprefix'] = 'Text to be prefixed to the subject line of all outgoing mail.';
  $string['configenablecalendarexport'] = 'Enable exporting or subscribing to calendars.';
  $string['configenablecomments'] = 'Enable comments';
  $string['configenablecourserequests'] = 'This will allow any user to request a course be created.';
@@@ -498,7 -499,6 +498,7 @@@ $string['editorspellinghelp'] = 'Enabl
  $string['editstrings'] = 'Edit words or phrases';
  $string['emailchangeconfirmation'] = 'Email change confirmation';
  $string['emailfromvia'] = 'Email via information';
 +$string['emailsubjectprefix'] = 'Email subject prefix text';
  $string['emoticontext'] = 'Text';
  $string['emoticonimagename'] = 'Image name';
  $string['emoticonalt'] = 'Alternative text';
@@@ -1216,6 -1216,7 +1216,7 @@@ $string['taskanalyticscleanup'] = 'Anal
  $string['taskautomatedbackup'] = 'Automated backups';
  $string['taskbackupcleanup'] = 'Clean backup tables and logs';
  $string['taskbadgescron'] = 'Award badges';
+ $string['taskbadgesmessagecron'] = 'Background processing for sending badges notifications';
  $string['taskblogcron'] = 'Sync external blogs';
  $string['taskcachecleanup'] = 'Remove expired cache entries';
  $string['taskcachecron'] = 'Background processing for caches';
@@@ -1234,7 -1235,6 +1235,7 @@@ $string['taskfiletrashcleanup'] = 'Clea
  $string['taskglobalsearchindex'] = 'Global search indexing';
  $string['taskglobalsearchoptimize'] = 'Global search index optimization';
  $string['taskgradecron'] = 'Background processing for gradebook';
 +$string['taskgradehistorycleanup'] = 'Background processing for clean grade history tables';
  $string['tasklegacycron'] = 'Legacy cron processing for plugins';
  $string['tasklogcleanup'] = 'Cleanup of task logs';
  $string['tasklogs'] = 'Task logs';
@@@ -1243,8 -1243,7 +1244,8 @@@ $string['taskpasswordresetcleanup'] = '
  $string['taskplagiarismcron'] = 'Background processing for legacy cron in plagiarism plugins';
  $string['taskportfoliocron'] = 'Background processing for portfolio plugins';
  $string['taskprocessing'] = 'Task processing';
 -$string['taskquestioncron'] = 'Background processing for question engine';
 +$string['taskquestioncron'] = 'Background processing for cleaning up the old question previews';
 +$string['taskquestionstatscleanupcron'] = 'Background processing for cleaning up the old question statistics cache';
  $string['taskrefreshsystemtokens'] = 'Refresh OAuth tokens for service accounts';
  $string['taskregistrationcron'] = 'Site registration';
  $string['tasksendfailedloginnotifications'] = 'Send failed login notifications';
@@@ -1413,6 -1412,3 +1414,6 @@@ $string['moodleorghubname'] = 'Moodle.n
  $string['hubs'] = 'Hubs';
  $string['configloginhttps'] = 'Turning this on will make Moodle use a secure https connection just for the login page (providing a secure login), and then afterwards revert back to the normal http URL for general speed.  CAUTION: this setting REQUIRES https to be specifically enabled on the web server - if it is not then YOU COULD LOCK YOURSELF OUT OF YOUR SITE.';
  $string['loginhttps'] = 'Use HTTPS for logins';
 +// Deprecated since Moodle 3.7.
 +$string['allowblockstodock'] = 'Allow blocks to use the dock';
 +$string['configallowblockstodock'] = 'If enabled and supported by the selected theme users can choose to move blocks to a special dock.';
diff --combined lib/badgeslib.php
@@@ -218,7 -218,6 +218,7 @@@ class badge 
       * @return array
       */
      public function get_accepted_criteria() {
 +        global $CFG;
          $criteriatypes = array();
  
          if ($this->type == BADGE_TYPE_COURSE) {
                      BADGE_CRITERIA_TYPE_MANUAL,
                      BADGE_CRITERIA_TYPE_COURSE,
                      BADGE_CRITERIA_TYPE_BADGE,
 -                    BADGE_CRITERIA_TYPE_ACTIVITY
 +                    BADGE_CRITERIA_TYPE_ACTIVITY,
 +                    BADGE_CRITERIA_TYPE_COMPETENCY
              );
          } else if ($this->type == BADGE_TYPE_SITE) {
              $criteriatypes = array(
                      BADGE_CRITERIA_TYPE_BADGE,
                      BADGE_CRITERIA_TYPE_PROFILE,
                      BADGE_CRITERIA_TYPE_COHORT,
 +                    BADGE_CRITERIA_TYPE_COMPETENCY
              );
          }
 +        $alltypes = badges_list_criteria();
 +        foreach ($criteriatypes as $index => $type) {
 +            if (!isset($alltypes[$type])) {
 +                unset($criteriatypes[$index]);
 +            }
 +        }
  
          return $criteriatypes;
      }
              }
  
              list($extrajoin, $extrawhere, $extraparams) = $crit->get_completed_criteria_sql();
 +
              // For site level badges, get all active site users who can earn this badge and haven't got it yet.
              if ($this->type == BADGE_TYPE_SITE) {
                  $sql = "SELECT DISTINCT u.id, bi.badgeid
          $badgecontext = $this->get_context();
          $fs->delete_area_files($badgecontext->id, 'badges', 'badgeimage', $this->id);
  
 -        // Delete endorsements, competencies and related badges.
 +        // Delete endorsements, alignments and related badges.
          $DB->delete_records('badge_endorsement', array('badgeid' => $this->id));
          $relatedsql = 'badgeid = :badgeid OR relatedbadgeid = :relatedbadgeid';
          $relatedparams = array(
              'relatedbadgeid' => $this->id
          );
          $DB->delete_records_select('badge_related', $relatedsql, $relatedparams);
 -        $DB->delete_records('badge_competencies', array('badgeid' => $this->id));
 +        $DB->delete_records('badge_alignment', array('badgeid' => $this->id));
  
          // Finally, remove badge itself.
          $DB->delete_records('badge', array('id' => $this->id));
      }
  
      /**
 -     * Insert/update competency alignment information of badge.
 +     * Insert/update alignment information of badge.
       *
 -     * @param stdClass $alignment Data of a competency alignment.
 -     * @param int $alignmentid ID competency alignment.
 +     * @param stdClass $alignment Data of a alignment.
 +     * @param int $alignmentid ID alignment.
       * @return bool|int A status/ID when insert or update data.
       */
      public function save_alignment($alignment, $alignmentid = 0) {
          global $DB;
  
 -        $record = $DB->record_exists('badge_competencies', array('id' => $alignmentid));
 +        $record = $DB->record_exists('badge_alignment', array('id' => $alignmentid));
          if ($record) {
              $alignment->id = $alignmentid;
 -            return $DB->update_record('badge_competencies', $alignment);
 +            return $DB->update_record('badge_alignment', $alignment);
          } else {
 -            return $DB->insert_record('badge_competencies', $alignment, true);
 +            return $DB->insert_record('badge_alignment', $alignment, true);
          }
      }
  
      /**
 -     * Delete a competency alignment of badge.
 +     * Delete a alignment of badge.
       *
 -     * @param int $alignmentid ID competency alignment.
 -     * @return bool A status for delete a competency alignment.
 +     * @param int $alignmentid ID alignment.
 +     * @return bool A status for delete a alignment.
       */
      public function delete_alignment($alignmentid) {
          global $DB;
 -        return $DB->delete_records('badge_competencies', array('badgeid' => $this->id, 'id' => $alignmentid));
 +        return $DB->delete_records('badge_alignment', array('badgeid' => $this->id, 'id' => $alignmentid));
      }
  
      /**
 -     * Get competencies of badge.
 +     * Get alignments of badge.
       *
 -     * @return array List content competencies.
 +     * @return array List content alignments.
       */
 -    public function get_alignment() {
 +    public function get_alignments() {
          global $DB;
 -        return $DB->get_records('badge_competencies', array('badgeid' => $this->id));
 +        return $DB->get_records('badge_alignment', array('badgeid' => $this->id));
 +    }
 +
 +    /**
 +     * Get alignments of badge.
 +     *
 +     * @deprecated since Moodle 3.7 see MDL-63876
 +     * @return array List content alignments.
 +     */
 +    public function get_alignment() {
 +        debugging('Use of get_alignment is deprecated. Call get_alignments instead.', DEBUG_DEVELOPER);
 +
 +        return $this->get_alignments();
      }
  
      /**
@@@ -1555,59 -1533,49 +1555,106 @@@ function badges_setup_backpack_js() 
      }
  }
  
 +/**
 + * Return all the enabled criteria types for this site.
 + *
 + * @param boolean $enabled
 + * @return array
 + */
 +function badges_list_criteria($enabled = true) {
 +    global $CFG;
 +
 +    $types = array(
 +        BADGE_CRITERIA_TYPE_OVERALL    => 'overall',
 +        BADGE_CRITERIA_TYPE_ACTIVITY   => 'activity',
 +        BADGE_CRITERIA_TYPE_MANUAL     => 'manual',
 +        BADGE_CRITERIA_TYPE_SOCIAL     => 'social',
 +        BADGE_CRITERIA_TYPE_COURSE     => 'course',
 +        BADGE_CRITERIA_TYPE_COURSESET  => 'courseset',
 +        BADGE_CRITERIA_TYPE_PROFILE    => 'profile',
 +        BADGE_CRITERIA_TYPE_BADGE      => 'badge',
 +        BADGE_CRITERIA_TYPE_COHORT     => 'cohort',
 +        BADGE_CRITERIA_TYPE_COMPETENCY => 'competency',
 +    );
 +    if ($enabled) {
 +        foreach ($types as $key => $type) {
 +            $class = 'award_criteria_' . $type;
 +            $file = $CFG->dirroot . '/badges/criteria/' . $class . '.php';
 +            if (file_exists($file)) {
 +                require_once($file);
 +
 +                if (!$class::is_enabled()) {
 +                    unset($types[$key]);
 +                }
 +            }
 +        }
 +    }
 +    return $types;
 +}
 +
 +/**
 + * Check if any badge has records for competencies.
 + *
 + * @param array $competencyids Array of competencies ids.
 + * @return boolean Return true if competencies were found in any badge.
 + */
 +function badge_award_criteria_competency_has_records_for_competencies($competencyids) {
 +    global $DB;
 +
 +    list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
 +
 +    $sql = "SELECT DISTINCT bc.badgeid
 +                FROM {badge_criteria} bc
 +                JOIN {badge_criteria_param} bcp ON bc.id = bcp.critid
 +                WHERE bc.criteriatype = :criteriatype AND bcp.value $insql";
 +    $params['criteriatype'] = BADGE_CRITERIA_TYPE_COMPETENCY;
 +
 +    return $DB->record_exists_sql($sql, $params);
 +}
++
+ /**
+  * Creates single message for all notification and sends it out
+  *
+  * @param object $badge A badge which is notified about.
+  */
+ function badge_assemble_notification(stdClass $badge) {
+     global $DB;
+     $userfrom = core_user::get_noreply_user();
+     $userfrom->maildisplay = true;
+     if ($msgs = $DB->get_records_select('badge_issued', 'issuernotified IS NULL AND badgeid = ?', array($badge->id))) {
+         // Get badge creator.
+         $creator = $DB->get_record('user', array('id' => $badge->creator), '*', MUST_EXIST);
+         $creatorsubject = get_string('creatorsubject', 'badges', $badge->name);
+         $creatormessage = '';
+         // Put all messages in one digest.
+         foreach ($msgs as $msg) {
+             $issuedlink = html_writer::link(new moodle_url('/badges/badge.php', array('hash' => $msg->uniquehash)), $badge->name);
+             $recipient = $DB->get_record('user', array('id' => $msg->userid), '*', MUST_EXIST);
+             $a = new stdClass();
+             $a->user = fullname($recipient);
+             $a->link = $issuedlink;
+             $creatormessage .= get_string('creatorbody', 'badges', $a);
+             $DB->set_field('badge_issued', 'issuernotified', time(), array('badgeid' => $msg->badgeid, 'userid' => $msg->userid));
+         }
+         // Create a message object.
+         $eventdata = new \core\message\message();
+         $eventdata->courseid          = SITEID;
+         $eventdata->component         = 'moodle';
+         $eventdata->name              = 'badgecreatornotice';
+         $eventdata->userfrom          = $userfrom;
+         $eventdata->userto            = $creator;
+         $eventdata->notification      = 1;
+         $eventdata->subject           = $creatorsubject;
+         $eventdata->fullmessage       = format_text_email($creatormessage, FORMAT_HTML);
+         $eventdata->fullmessageformat = FORMAT_PLAIN;
+         $eventdata->fullmessagehtml   = $creatormessage;
+         $eventdata->smallmessage      = $creatorsubject;
+         message_send($eventdata);
+     }
+ }
diff --combined lib/db/tasks.php
@@@ -149,15 -149,6 +149,15 @@@ $tasks = array
          'dayofweek' => '*',
          'month' => '*'
      ),
 +    array(
 +        'classname' => 'core\task\grade_history_cleanup_task',
 +        'blocking' => 0,
 +        'minute' => '*',
 +        'hour' => '0',
 +        'day' => '*',
 +        'dayofweek' => '*',
 +        'month' => '*'
 +    ),
      array(
          'classname' => 'core\task\completion_regular_task',
          'blocking' => 0,
          'month' => '*'
      ),
      array(
 -        'classname' => 'core\task\question_cron_task',
 +        'classname' => 'core\task\question_preview_cleanup_task',
 +        'blocking' => 0,
 +        'minute' => '*',
 +        'hour' => '*',
 +        'day' => '*',
 +        'dayofweek' => '*',
 +        'month' => '*'
 +    ),
 +    array(
 +        'classname' => 'core\task\question_stats_cleanup_task',
          'blocking' => 0,
          'minute' => '*',
          'hour' => '*',
          'dayofweek' => '*',
          'month' => '*'
      ),
+     array(
+         'classname' => 'core\task\badges_message_task',
+         'blocking' => 0,
+         'minute' => '*/5',
+         'hour' => '*',
+         'day' => '*',
+         'dayofweek' => '*',
+         'month' => '*'
+     ),
      array(
          'classname' => 'core\task\file_temp_cleanup_task',
          'blocking' => 0,
diff --combined version.php
  
  defined('MOODLE_INTERNAL') || die();
  
- $version  = 2019040600.02;              // YYYYMMDD      = weekly release date of this DEV branch.
 -$version  = 2019032200.01;              // YYYYMMDD      = weekly release date of this DEV branch.
++$version  = 2019040600.03;              // YYYYMMDD      = weekly release date of this DEV branch.
                                          //         RR    = release increments - 00 in DEV branches.
                                          //           .XX = incremental changes.
  
 -$release  = '3.7dev (Build: 20190322)'; // Human-friendly version name
 +$release  = '3.7dev+ (Build: 20190406)'; // Human-friendly version name
  
  $branch   = '37';                       // This version's branch.
  $maturity = MATURITY_ALPHA;             // This version's maturity level.