Merge branch 'MDL-54751-master-v5' of https://github.com/snake/moodle

This commit is contained in:
David Monllao 2016-11-07 08:55:44 +08:00
commit 90abff01b3
25 changed files with 725 additions and 25 deletions

View file

@ -1063,7 +1063,7 @@ class completion_info {
$modinfo = get_fast_modinfo($this->course);
$result = array();
foreach ($modinfo->get_cms() as $cm) {
if ($cm->completion != COMPLETION_TRACKING_NONE) {
if ($cm->completion != COMPLETION_TRACKING_NONE && !$cm->deletioninprogress) {
$result[$cm->id] = $cm;
}
}

View file

@ -300,6 +300,7 @@
<FIELD NAME="completionexpected" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Date at which students are expected to complete this activity. This field is used when displaying student progress."/>
<FIELD NAME="showdescription" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Some module types support a 'description' which shows within the module pages. This option controls whether it also displays on the course main page. 0 = does not display (default), 1 = displays"/>
<FIELD NAME="availability" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Availability restrictions for viewing this activity, in JSON format. Null if no restrictions."/>
<FIELD NAME="deletioninprogress" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View file

@ -2392,5 +2392,19 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2016110500.00);
}
if ($oldversion < 2016110600.00) {
// Define a field 'deletioninprogress' in the 'course_modules' table, to background deletion tasks.
$table = new xmldb_table('course_modules');
$field = new xmldb_field('deletioninprogress', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'availability');
// Conditionally launch add field 'deletioninprogress'.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2016110600.00);
}
return true;
}

View file

@ -2215,7 +2215,12 @@ class file_storage {
mkdir($trashpath, $this->dirpermissions, true);
}
rename($contentfile, $trashfile);
chmod($trashfile, $this->filepermissions); // fix permissions if needed
// Fix permissions, only if needed.
$currentperms = octdec(substr(decoct(fileperms($trashfile)), -4));
if ((int)$this->filepermissions !== $currentperms) {
chmod($trashfile, $this->filepermissions);
}
}
/**

View file

@ -524,6 +524,14 @@ class grade_item extends grade_object {
* @return bool Locked state
*/
public function is_locked($userid=NULL) {
global $CFG;
// Override for any grade items belonging to activities which are in the process of being deleted.
require_once($CFG->dirroot . '/course/lib.php');
if (course_module_instance_pending_deletion($this->courseid, $this->itemmodule, $this->iteminstance)) {
return true;
}
if (!empty($this->locked)) {
return true;
}
@ -1393,7 +1401,12 @@ class grade_item extends grade_object {
public function get_name($fulltotal=false) {
if (strval($this->itemname) !== '') {
// MDL-10557
return format_string($this->itemname);
// Make it obvious to users if the course module to which this grade item relates, is currently being removed.
$deletionpending = course_module_instance_pending_deletion($this->courseid, $this->itemmodule, $this->iteminstance);
$deletionnotice = get_string('gradesmoduledeletionprefix', 'grades');
return $deletionpending ? format_string($deletionnotice . ' ' . $this->itemname) : format_string($this->itemname);
} else if ($this->is_course_item()) {
return get_string('coursetotal', 'grades');

View file

@ -554,8 +554,7 @@ class course_modinfo {
// Get section data
$sections = $DB->get_records('course_sections', array('course' => $course->id), 'section',
'section, id, course, name, summary, summaryformat, sequence, visible, ' .
'availability');
'section, id, course, name, summary, summaryformat, sequence, visible, availability');
$compressedsections = array();
$formatoptionsdef = course_get_format($course)->section_format_options();
@ -753,6 +752,7 @@ class course_modinfo {
* @property-read mixed $customdata Optional custom data stored in modinfo cache for this activity, or null if none
* @property-read string $afterlink Extra HTML code to display after link - calculated on request
* @property-read string $afterediticons Extra HTML code to display after editing icons (e.g. more icons) - calculated on request
* @property-read bool $deletioninprogress True if this course module is scheduled for deletion, false otherwise.
*/
class cm_info implements IteratorAggregate {
/**
@ -1038,6 +1038,11 @@ class cm_info implements IteratorAggregate {
*/
private $afterediticons;
/**
* @var bool representing the deletion state of the module. True if the mod is scheduled for deletion.
*/
private $deletioninprogress;
/**
* List of class read-only properties and their getter methods.
* Used by magic functions __get(), __isset(), __empty()
@ -1089,6 +1094,7 @@ class cm_info implements IteratorAggregate {
'uservisible' => 'get_user_visible',
'visible' => false,
'visibleold' => false,
'deletioninprogress' => false
);
/**
@ -1505,7 +1511,7 @@ class cm_info implements IteratorAggregate {
static $cmfields = array('id', 'course', 'module', 'instance', 'section', 'idnumber', 'added',
'score', 'indent', 'visible', 'visibleold', 'groupmode', 'groupingid',
'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected',
'showdescription', 'availability');
'showdescription', 'availability', 'deletioninprogress');
foreach ($cmfields as $key) {
$cmrecord->$key = $this->$key;
}
@ -1700,6 +1706,7 @@ class cm_info implements IteratorAggregate {
$this->added = isset($mod->added) ? $mod->added : 0;
$this->score = isset($mod->score) ? $mod->score : 0;
$this->visibleold = isset($mod->visibleold) ? $mod->visibleold : 0;
$this->deletioninprogress = isset($mod->deletioninprogress) ? $mod->deletioninprogress : 0;
// Note: it saves effort and database space to always include the
// availability and completion fields, even if availability or completion
@ -1861,6 +1868,12 @@ class cm_info implements IteratorAggregate {
}
$this->uservisible = true;
// If the module is being deleted, set the uservisible state to false and return.
if ($this->deletioninprogress) {
$this->uservisible = false;
return null;
}
// If the user cannot access the activity set the uservisible flag to false.
// Additional checks are required to determine whether the activity is entirely hidden or just greyed out.
if ((!$this->visible or !$this->get_available()) and
@ -2465,7 +2478,7 @@ class section_info implements IteratorAggregate {
'summary' => '',
'summaryformat' => '1', // FORMAT_HTML, but must be a string
'visible' => '1',
'availability' => null,
'availability' => null
);
/**

View file

@ -822,4 +822,21 @@ class phpunit_util extends testing_util {
return 'en_AU.UTF-8';
}
}
/**
* Executes all adhoc tasks in the queue. Useful for testing asynchronous behaviour.
*
* @return void
*/
public static function run_all_adhoc_tasks() {
$now = time();
while (($task = \core\task\manager::get_next_adhoc_task($now)) !== null) {
try {
$task->execute();
\core\task\manager::adhoc_task_complete($task);
} catch (Exception $e) {
\core\task\manager::adhoc_task_failed($task);
}
}
}
}

View file

@ -1001,6 +1001,43 @@ class behat_general extends behat_base {
}
}
/**
* Runs all ad-hoc tasks in the queue.
*
* This is faster and more reliable than running cron (running cron won't
* work more than once in the same test, for instance). However it is
* a little less 'realistic'.
*
* While the task is running, we suppress mtrace output because it makes
* the Behat result look ugly.
*
* @Given /^I run all adhoc tasks$/
* @throws DriverException
*/
public function i_run_all_adhoc_tasks() {
// Do setup for cron task.
cron_setup_user();
// Run tasks. Locking is handled by get_next_adhoc_task.
$now = time();
ob_start(); // Discard task output as not appropriate for Behat output!
while (($task = \core\task\manager::get_next_adhoc_task($now)) !== null) {
try {
$task->execute();
// Mark task complete.
\core\task\manager::adhoc_task_complete($task);
} catch (Exception $e) {
// Mark task failed and throw exception.
\core\task\manager::adhoc_task_failed($task);
ob_end_clean();
throw new DriverException('An adhoc task failed', 0, $e);
}
}
ob_end_clean();
}
/**
* Checks that an element and selector type exists in another element and selector type on the current page.
*