Merge branch 'MDL-58399-master' of git://github.com/jleyva/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 5 Apr 2017 06:22:39 +0000 (14:22 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Wed, 5 Apr 2017 06:22:39 +0000 (14:22 +0800)
1  2 
course/externallib.php
lib/filestorage/stored_file.php
mod/book/lib.php
mod/folder/lib.php
mod/forum/tests/externallib_test.php
mod/imscp/lib.php
mod/page/lib.php
mod/resource/lib.php

diff --combined course/externallib.php
@@@ -160,7 -160,7 +160,7 @@@ class core_course_external extends exte
              //retrieve sections
              $modinfo = get_fast_modinfo($course);
              $sections = $modinfo->get_section_info_all();
 -            $coursenumsections = course_get_format($course)->get_course()->numsections;
 +            $coursenumsections = course_get_format($course)->get_last_section_number();
  
              //for each sections (first displayed to last displayed)
              $modinfosections = $modinfo->get_sections();
                                                    'timecreated' => new external_value(PARAM_INT, 'Time created'),
                                                    'timemodified' => new external_value(PARAM_INT, 'Time modified'),
                                                    'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
+                                                   'mimetype' => new external_value(PARAM_RAW, 'File mime type.', VALUE_OPTIONAL),
+                                                   'isexternalfile' => new external_value(PARAM_BOOL, 'Whether is an external file.',
+                                                     VALUE_OPTIONAL),
+                                                   'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for external files.',
+                                                     VALUE_OPTIONAL),
  
                                                    // copyright related info
                                                    'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
                      // For backward-compartibility
                      $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
                  }
 +                // Return numsections for backward-compatibility with clients who expect it.
 +                $courseinfo['numsections'] = course_get_format($course)->get_last_section_number();
                  $courseinfo['groupmode'] = $course->groupmode;
                  $courseinfo['groupmodeforce'] = $course->groupmodeforce;
                  $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
      /**
       * Returns description of method parameters
       *
 +     * @deprecated since 3.3
 +     *
       * @return external_function_parameters
       * @since Moodle 3.2
       */
      /**
       * Return activities overview for the given courses.
       *
 +     * @deprecated since 3.3
 +     *
       * @param array $courseids a list of course ids
       * @return array of warnings and the activities overview
       * @since Moodle 3.2
      /**
       * Returns description of method result value
       *
 +     * @deprecated since 3.3
 +     *
       * @return external_description
       * @since Moodle 3.2
       */
          );
      }
  
 +    /**
 +     * Marking the method as deprecated.
 +     *
 +     * @return bool
 +     */
 +    public static function get_activities_overview_is_deprecated() {
 +        return true;
 +    }
 +
      /**
       * Returns description of method parameters
       *
@@@ -102,15 -102,6 +102,15 @@@ class stored_file 
          return !empty($this->repository);
      }
  
 +    /**
 +     * Whether or not this is a controlled link. Note that repositories cannot support FILE_REFERENCE and FILE_CONTROLLED_LINK.
 +     *
 +     * @return bool
 +     */
 +    public function is_controlled_link() {
 +        return $this->is_external_file() && $this->repository->supported_returntypes() & FILE_CONTROLLED_LINK;
 +    }
 +
      /**
       * Update some file record fields
       * NOTE: Must remain protected
          }
      }
  
+     /**
+      * Returns repository type.
+      *
+      * @return mixed str|null the repository type or null if is not an external file
+      * @since  Moodle 3.3
+      */
+     public function get_repository_type() {
+         if (!empty($this->repository)) {
+             return $this->repository->get_typename();
+         } else {
+             return null;
+         }
+     }
      /**
       * get reference file id
       * @return int
diff --combined mod/book/lib.php
@@@ -88,12 -88,7 +88,12 @@@ function book_add_instance($data, $mfor
          $data->customtitles = 0;
      }
  
 -    return $DB->insert_record('book', $data);
 +    $id = $DB->insert_record('book', $data);
 +
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($data->coursemodule, 'book', $id, $completiontimeexpected);
 +
 +    return $id;
  }
  
  /**
@@@ -117,9 -112,6 +117,9 @@@ function book_update_instance($data, $m
      $book = $DB->get_record('book', array('id'=>$data->id));
      $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($data->coursemodule, 'book', $book->id, $completiontimeexpected);
 +
      return true;
  }
  
@@@ -136,9 -128,6 +136,9 @@@ function book_delete_instance($id) 
          return false;
      }
  
 +    $cm = get_coursemodule_from_instance('book', $id);
 +    \core_completion\api::update_completion_date_event($cm->id, 'book', $id, null);
 +
      $DB->delete_records('book_chapters', array('bookid'=>$book->id));
      $DB->delete_records('book', array('id'=>$book->id));
  
@@@ -595,6 -584,11 +595,11 @@@ function book_export_contents($cm, $bas
              $file['userid']       = $fileinfo->get_userid();
              $file['author']       = $fileinfo->get_author();
              $file['license']      = $fileinfo->get_license();
+             $file['mimetype']     = $fileinfo->get_mimetype();
+             $file['isexternalfile'] = $fileinfo->is_external_file();
+             if ($file['isexternalfile']) {
+                 $file['repositorytype'] = $fileinfo->get_repository_type();
+             }
              $contents[] = $file;
          }
      }
@@@ -697,37 -691,3 +702,37 @@@ function mod_book_get_fontawesome_icon_
          'mod_book:nav_exit' => 'fa-arrow-up',
      ];
  }
 +
 +/**
 + * Handles creating actions for events.
 + *
 + * @param calendar_event $event
 + * @param \core_calendar\action_factory $factory
 + * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
 + */
 +function mod_book_core_calendar_provide_event_action(calendar_event $event,
 +                                                     \core_calendar\action_factory $factory) {
 +    $cm = get_fast_modinfo($event->courseid)->instances['book'][$event->instance];
 +    $context = context_module::instance($cm->id);
 +
 +    if (!has_capability('mod/book:read', $context)) {
 +        return null;
 +    }
 +
 +    $course = new stdClass();
 +    $course->id = $event->courseid;
 +    $completion = new \completion_info($course);
 +
 +    $completiondata = $completion->get_data($cm, false);
 +
 +    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
 +        return null;
 +    }
 +
 +    return $factory->create_instance(
 +        get_string('view'),
 +        new \moodle_url('/mod/book/view.php', ['id' => $cm->id]),
 +        1,
 +        true
 +    );
 +}
diff --combined mod/folder/lib.php
@@@ -119,9 -119,6 +119,9 @@@ function folder_add_instance($data, $mf
          file_save_draft_area_files($draftitemid, $context->id, 'mod_folder', 'content', 0, array('subdirs'=>true));
      }
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($data->coursemodule, 'folder', $data->id, $completiontimeexpected);
 +
      return $data->id;
  }
  
@@@ -148,9 -145,6 +148,9 @@@ function folder_update_instance($data, 
          file_save_draft_area_files($draftitemid, $context->id, 'mod_folder', 'content', 0, array('subdirs'=>true));
      }
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($data->coursemodule, 'folder', $data->id, $completiontimeexpected);
 +
      return true;
  }
  
@@@ -166,9 -160,6 +166,9 @@@ function folder_delete_instance($id) 
          return false;
      }
  
 +    $cm = get_coursemodule_from_instance('folder', $id);
 +    \core_completion\api::update_completion_date_event($cm->id, 'folder', $folder->id, null);
 +
      // note: all context files are deleted automatically
  
      $DB->delete_records('folder', array('id'=>$folder->id));
@@@ -326,6 -317,11 +326,11 @@@ function folder_export_contents($cm, $b
          $file['userid']       = $fileinfo->get_userid();
          $file['author']       = $fileinfo->get_author();
          $file['license']      = $fileinfo->get_license();
+         $file['mimetype']     = $fileinfo->get_mimetype();
+         $file['isexternalfile'] = $fileinfo->is_external_file();
+         if ($file['isexternalfile']) {
+             $file['repositorytype'] = $fileinfo->get_repository_type();
+         }
          $contents[] = $file;
      }
  
@@@ -770,32 -766,3 +775,32 @@@ function folder_check_updates_since(cm_
      $updates = course_check_module_updates_since($cm, $from, array('content'), $filter);
      return $updates;
  }
 +
 +/**
 + * Handles creating actions for events.
 + *
 + * @param calendar_event $event
 + * @param \core_calendar\action_factory $factory
 + * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
 + */
 +function mod_folder_core_calendar_provide_event_action(calendar_event $event,
 +                                                     \core_calendar\action_factory $factory) {
 +    $cm = get_fast_modinfo($event->courseid)->instances['folder'][$event->instance];
 +
 +    $course = new stdClass();
 +    $course->id = $event->courseid;
 +    $completion = new \completion_info($course);
 +
 +    $completiondata = $completion->get_data($cm, false);
 +
 +    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
 +        return null;
 +    }
 +
 +    return $factory->create_instance(
 +        get_string('view'),
 +        new \moodle_url('/mod/folder/view.php', ['id' => $cm->id]),
 +        1,
 +        true
 +    );
 +}
@@@ -94,7 -94,6 +94,7 @@@ class mod_forum_external_testcase exten
          $forum1->numdiscussions = 1;
          $forum1->cancreatediscussions = true;
          $forum1->istracked = true;
 +        $forum1->unreadpostscount = 0;
          $forum1->introfiles = [];
  
          $record = new stdClass();
          $record = new stdClass();
          $record->course = $course1->id;
          $forum2 = self::getDataGenerator()->create_module('forum', $record);
 +        $forum2cm = get_coursemodule_from_id('forum', $forum2->cmid);
          $forum2context = context_module::instance($forum2->cmid);
  
          // Add discussions to the forums.
                                      $discussion1reply1->id, '/', $filename),
                      'timemodified' => $timepost,
                      'mimetype' => 'image/jpeg',
+                     'isexternalfile' => false,
                  )
              ),
              'totalscore' => $discussion1reply1->totalscore,
          array_pop($posts['posts']);
          $this->assertEquals($expectedposts, $posts);
  
 +        // Check we receive the unread count correctly on tracked forum.
 +        forum_tp_count_forum_unread_posts($forum2cm, $course1, true);    // Reset static cache.
 +        $result = mod_forum_external::get_forums_by_courses(array($course1->id));
 +        $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
 +        foreach ($result as $f) {
 +            if ($f['id'] == $forum2->id) {
 +                $this->assertEquals(1, $f['unreadpostscount']);
 +            }
 +        }
 +
          // Test discussion without additional posts. There should be only one post (the one created by the discussion).
          $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC');
          $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
          foreach ($posts['posts'] as $post) {
              $this->assertTrue($post['postread']);
          }
 +
 +        // Check we receive 0 unread posts.
 +        forum_tp_count_forum_unread_posts($forum2cm, $course1, true);    // Reset static cache.
 +        $result = mod_forum_external::get_forums_by_courses(array($course1->id));
 +        $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
 +        foreach ($result as $f) {
 +            if ($f['id'] == $forum2->id) {
 +                $this->assertEquals(0, $f['unreadpostscount']);
 +            }
 +        }
      }
  
      /**
diff --combined mod/imscp/lib.php
@@@ -133,9 -133,6 +133,9 @@@ function imscp_add_instance($data, $mfo
          }
      }
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($cmid, 'imscp', $data->id, $completiontimeexpected);
 +
      return $data->id;
  }
  
@@@ -199,9 -196,6 +199,9 @@@ function imscp_update_instance($data, $
      $imscp->structure = is_array($structure) ? serialize($structure) : null;
      $DB->update_record('imscp', $imscp);
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($cmid, 'imscp', $imscp->id, $completiontimeexpected);
 +
      return true;
  }
  
@@@ -217,9 -211,6 +217,9 @@@ function imscp_delete_instance($id) 
          return false;
      }
  
 +    $cm = get_coursemodule_from_instance('imscp', $id);
 +    \core_completion\api::update_completion_date_event($cm->id, 'imscp', $id, null);
 +
      // Note: all context files are deleted automatically.
  
      $DB->delete_records('imscp', array('id' => $imscp->id));
@@@ -420,6 -411,11 +420,11 @@@ function imscp_export_contents($cm, $ba
          $file['userid']       = $fileinfo->get_userid();
          $file['author']       = $fileinfo->get_author();
          $file['license']      = $fileinfo->get_license();
+         $file['mimetype']     = $fileinfo->get_mimetype();
+         $file['isexternalfile'] = $fileinfo->is_external_file();
+         if ($file['isexternalfile']) {
+             $file['repositorytype'] = $fileinfo->get_repository_type();
+         }
          $contents[] = $file;
      }
  
@@@ -467,32 -463,3 +472,32 @@@ function imscp_check_updates_since(cm_i
      $updates = course_check_module_updates_since($cm, $from, array('content'), $filter);
      return $updates;
  }
 +
 +/**
 + * Handles creating actions for events.
 + *
 + * @param calendar_event $event
 + * @param \core_calendar\action_factory $factory
 + * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
 + */
 +function mod_imscp_core_calendar_provide_event_action(calendar_event $event,
 +                                                      \core_calendar\action_factory $factory) {
 +    $cm = get_fast_modinfo($event->courseid)->instances['imscp'][$event->instance];
 +
 +    $course = new stdClass();
 +    $course->id = $event->courseid;
 +    $completion = new \completion_info($course);
 +
 +    $completiondata = $completion->get_data($cm, false);
 +
 +    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
 +        return null;
 +    }
 +
 +    return $factory->create_instance(
 +        get_string('view'),
 +        new \moodle_url('/mod/imscp/view.php', ['id' => $cm->id]),
 +        1,
 +        true
 +    );
 +}
diff --combined mod/page/lib.php
@@@ -128,9 -128,6 +128,9 @@@ function page_add_instance($data, $mfor
          $DB->update_record('page', $data);
      }
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($cmid, 'page', $data->id, $completiontimeexpected);
 +
      return $data->id;
  }
  
@@@ -171,9 -168,6 +171,9 @@@ function page_update_instance($data, $m
          $DB->update_record('page', $data);
      }
  
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($cmid, 'page', $data->id, $completiontimeexpected);
 +
      return true;
  }
  
@@@ -189,9 -183,6 +189,9 @@@ function page_delete_instance($id) 
          return false;
      }
  
 +    $cm = get_coursemodule_from_instance('page', $id);
 +    \core_completion\api::update_completion_date_event($cm->id, 'page', $id, null);
 +
      // note: all context files are deleted automatically
  
      $DB->delete_records('page', array('id'=>$page->id));
@@@ -424,6 -415,11 +424,11 @@@ function page_export_contents($cm, $bas
          $file['userid']       = $fileinfo->get_userid();
          $file['author']       = $fileinfo->get_author();
          $file['license']      = $fileinfo->get_license();
+         $file['mimetype']     = $fileinfo->get_mimetype();
+         $file['isexternalfile'] = $fileinfo->is_external_file();
+         if ($file['isexternalfile']) {
+             $file['repositorytype'] = $fileinfo->get_repository_type();
+         }
          $contents[] = $file;
      }
  
@@@ -531,32 -527,3 +536,32 @@@ function page_check_updates_since(cm_in
      $updates = course_check_module_updates_since($cm, $from, array('content'), $filter);
      return $updates;
  }
 +
 +/**
 + * Handles creating actions for events.
 + *
 + * @param calendar_event $event
 + * @param \core_calendar\action_factory $factory
 + * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
 + */
 +function mod_page_core_calendar_provide_event_action(calendar_event $event,
 +                                                      \core_calendar\action_factory $factory) {
 +    $cm = get_fast_modinfo($event->courseid)->instances['page'][$event->instance];
 +
 +    $course = new stdClass();
 +    $course->id = $event->courseid;
 +    $completion = new \completion_info($course);
 +
 +    $completiondata = $completion->get_data($cm, false);
 +
 +    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
 +        return null;
 +    }
 +
 +    return $factory->create_instance(
 +        get_string('view'),
 +        new \moodle_url('/mod/page/view.php', ['id' => $cm->id]),
 +        1,
 +        true
 +    );
 +}
diff --combined mod/resource/lib.php
@@@ -109,10 -109,6 +109,10 @@@ function resource_add_instance($data, $
      // we need to use context now, so we need to make sure all needed info is already in db
      $DB->set_field('course_modules', 'instance', $data->id, array('id'=>$cmid));
      resource_set_mainfile($data);
 +
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($cmid, 'resource', $data->id, $completiontimeexpected);
 +
      return $data->id;
  }
  
@@@ -133,10 -129,6 +133,10 @@@ function resource_update_instance($data
  
      $DB->update_record('resource', $data);
      resource_set_mainfile($data);
 +
 +    $completiontimeexpected = !empty($data->completionexpected) ? $data->completionexpected : null;
 +    \core_completion\api::update_completion_date_event($data->coursemodule, 'resource', $data->id, $completiontimeexpected);
 +
      return true;
  }
  
@@@ -180,9 -172,6 +180,9 @@@ function resource_delete_instance($id) 
          return false;
      }
  
 +    $cm = get_coursemodule_from_instance('resource', $id);
 +    \core_completion\api::update_completion_date_event($cm->id, 'resource', $id, null);
 +
      // note: all context files are deleted automatically
  
      $DB->delete_records('resource', array('id'=>$resource->id));
@@@ -460,6 -449,11 +460,11 @@@ function resource_export_contents($cm, 
          $file['userid']       = $fileinfo->get_userid();
          $file['author']       = $fileinfo->get_author();
          $file['license']      = $fileinfo->get_license();
+         $file['mimetype']     = $fileinfo->get_mimetype();
+         $file['isexternalfile'] = $fileinfo->is_external_file();
+         if ($file['isexternalfile']) {
+             $file['repositorytype'] = $fileinfo->get_repository_type();
+         }
          $contents[] = $file;
      }
  
@@@ -546,32 -540,3 +551,32 @@@ function resource_check_updates_since(c
      $updates = course_check_module_updates_since($cm, $from, array('content'), $filter);
      return $updates;
  }
 +
 +/**
 + * Handles creating actions for events.
 + *
 + * @param calendar_event $event
 + * @param \core_calendar\action_factory $factory
 + * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
 + */
 +function mod_resource_core_calendar_provide_event_action(calendar_event $event,
 +                                                      \core_calendar\action_factory $factory) {
 +    $cm = get_fast_modinfo($event->courseid)->instances['resource'][$event->instance];
 +
 +    $course = new stdClass();
 +    $course->id = $event->courseid;
 +    $completion = new \completion_info($course);
 +
 +    $completiondata = $completion->get_data($cm, false);
 +
 +    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
 +        return null;
 +    }
 +
 +    return $factory->create_instance(
 +        get_string('view'),
 +        new \moodle_url('/mod/resource/view.php', ['id' => $cm->id]),
 +        1,
 +        true
 +    );
 +}