# This patch file was generated by NetBeans IDE
# This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
# It uses platform neutral UTF-8 encoding.
# Above lines and this line are ignored by the patching process.
Index: moodle/backup/moodle2/backup_activity_task.class.php
--- moodle/backup/moodle2/backup_activity_task.class.php Base (1.7)
+++ moodle/backup/moodle2/backup_activity_task.class.php Locally Modified (Based On 1.7)
@@ -173,7 +173,7 @@
         // Find activity_included_setting
         if (!$this->get_setting_value('included')) {
             $this->log('activity skipped by _included setting', backup::LOG_DEBUG, $this->name);
-
+            $this->plan->set_excluding_activities();
         } else { // Setting tells us it's ok to execute
             parent::execute();
         }
Index: moodle/backup/moodle2/backup_final_task.class.php
--- moodle/backup/moodle2/backup_final_task.class.php Base (1.10)
+++ moodle/backup/moodle2/backup_final_task.class.php Locally Modified (Based On 1.10)
@@ -73,6 +73,9 @@
         // execute_condition() so only will be excuted if ALL module grade_items in course have been exported
         $this->add_step(new backup_gradebook_structure_step('course_gradebook','gradebook.xml'));
 
+        // Generate the course completion
+        $this->add_step(new backup_course_completion_structure_step('course_completion', 'completion.xml'));
+
         // Generate the scales file with all the (final) annotated scales
         $this->add_step(new backup_final_scales_structure_step('scaleslist', 'scales.xml'));
 
Index: moodle/backup/moodle2/backup_stepslib.php
--- moodle/backup/moodle2/backup_stepslib.php Base (1.44)
+++ moodle/backup/moodle2/backup_stepslib.php Locally Modified (Based On 1.44)
@@ -1535,3 +1535,82 @@
         return $book;
     }
 }
+
+/**
+ * Backups up the course completion information for the course.
+ */
+class backup_course_completion_structure_step extends backup_structure_step {
+
+    protected function execute_condition() {
+        // Check that all activities have been included
+        if ($this->task->is_excluding_activities()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * The structure of the course completion backup
+     *
+     * @return backup_nested_element
+     */
+    protected function define_structure() {
+
+        // To know if we are including user completion info
+        $userinfo = $this->get_setting_value('userscompletion');
+
+        $cc = new backup_nested_element('course_completion');
+
+        $criteria = new backup_nested_element('course_completion_criteria', array('id'), array(
+            'course','criteriatype', 'module', 'moduleinstance', 'courseinstanceshortname', 'enrolperiod', 'timeend', 'gradepass', 'role'
+        ));
+
+        $criteriacompletions = new backup_nested_element('course_completion_crit_completions');
+
+        $criteriacomplete = new backup_nested_element('course_completion_crit_compl', array('id'), array(
+            'userid','gradefinal','unenrolled','deleted','timecompleted'
+        ));
+
+        $coursecompletions = new backup_nested_element('course_completions', array('id'), array(
+            'userid', 'course', 'deleted', 'timenotified', 'timeenrolled','timestarted','timecompleted','reaggregate'
+        ));
+
+        $notify = new backup_nested_element('course_completion_notify', array('id'), array(
+            'course','role','message','timesent'
+        ));
+
+        $aggregatemethod = new backup_nested_element('course_completion_aggr_methd', array('id'), array(
+            'course','criteriatype','method','value'
+        ));
+
+        $cc->add_child($criteria);
+            $criteria->add_child($criteriacompletions);
+                $criteriacompletions->add_child($criteriacomplete);
+        $cc->add_child($coursecompletions);
+        $cc->add_child($notify);
+        $cc->add_child($aggregatemethod);
+
+        // We need to get the courseinstances shortname rather than an ID for restore
+        $criteria->set_source_sql("SELECT ccc.*, c.shortname courseinstanceshortname
+                                   FROM {course_completion_criteria} ccc
+                                   LEFT JOIN {course} c ON c.id = ccc.courseinstance
+                                   WHERE ccc.course = ?", array(backup::VAR_COURSEID));
+
+
+        $notify->set_source_table('course_completion_notify', array('course' => backup::VAR_COURSEID));
+        $aggregatemethod->set_source_table('course_completion_aggr_methd', array('course' => backup::VAR_COURSEID));
+
+        if ($userinfo) {
+            $criteriacomplete->set_source_table('course_completion_crit_compl', array('criteriaid' => backup::VAR_PARENTID));
+            $coursecompletions->set_source_table('course_completions', array('course' => backup::VAR_COURSEID));
+        }
+
+        $criteria->annotate_ids('role', 'role');
+        $criteriacomplete->annotate_ids('user', 'userid');
+        $coursecompletions->annotate_ids('user', 'userid');
+        $notify->annotate_ids('role', 'role');
+
+        return $cc;
+
+    }
+}
\ No newline at end of file
Index: moodle/backup/moodle2/restore_final_task.class.php
--- moodle/backup/moodle2/restore_final_task.class.php Base (1.11)
+++ moodle/backup/moodle2/restore_final_task.class.php Locally Modified (Based On 1.11)
@@ -47,6 +47,9 @@
             $this->add_step(new restore_gradebook_structure_step('gradebook_step','gradebook.xml'));
         }
 
+        // Course completion
+        $this->add_step(new restore_course_completion_structure_step('course_completion', 'completion.xml'));
+
         // Review all the module_availability records in backup_ids in order
         // to match them with existing modules / grade items.
         $this->add_step(new restore_process_course_modules_availability('process_modules_availability'));
Index: moodle/backup/moodle2/restore_stepslib.php
--- moodle/backup/moodle2/restore_stepslib.php Base (1.38)
+++ moodle/backup/moodle2/restore_stepslib.php Locally Modified (Based On 1.38)
@@ -1215,7 +1215,249 @@
     }
 }
 
+class restore_course_completion_structure_step extends restore_structure_step {
+
 /**
+     * Conditionally decide if this step should be executed.
+     *
+     * This function checks parameters that are not immediate settings to ensure
+     * that the enviroment is suitable for the restore of course completion info.
+     *
+     * This function checks the following four parameters:
+     *
+     *   1. Course completion is enabled on the site
+     *   2. The backup includes course completion information
+     *   3. All modules are restorable
+     *   4. All modules are marked for restore.
+     *
+     * @return bool True is safe to execute, false otherwise
+     */
+    protected function execute_condition() {
+        global $CFG;
+
+        // First check course completion is enabled on this site
+        if (empty($CFG->enablecompletion)) {
+            // Disabled, don't restore course completion
+            return false;
+        }
+
+        // Check it is included in the backup
+        $fullpath = $this->task->get_taskbasepath();
+        $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
+        if (!file_exists($fullpath)) {
+            // Not found, can't restore course completion
+            return false;
+        }
+
+        // Check we are able to restore all backed up modules
+        if ($this->task->is_missing_modules()) {
+            return false;
+        }
+
+        // Finally check all modules within the backup are being restored.
+        if ($this->task->is_excluding_activities()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Define the course completion structure
+     *
+     * @return array Array of restore_path_element
+     */
+    protected function define_structure() {
+
+        // To know if we are including user completion info
+        $userinfo = $this->get_setting_value('userscompletion');
+
+        $paths = array();
+        $paths[] = new restore_path_element('course_completion_criteria', '/course_completion/course_completion_criteria');
+        $paths[] = new restore_path_element('course_completion_notify', '/course_completion/course_completion_notify');
+        $paths[] = new restore_path_element('course_completion_aggr_methd', '/course_completion/course_completion_aggr_methd');
+
+        if ($userinfo) {
+            $paths[] = new restore_path_element('course_completion_crit_compl', '/course_completion/course_completion_criteria/course_completion_crit_completions/course_completion_crit_compl');
+            $paths[] = new restore_path_element('course_completions', '/course_completion/course_completions');
+        }
+
+        return $paths;
+
+    }
+
+    /**
+     * Process course completion criteria
+     *
+     * @global moodle_database $DB
+     * @param stdClass $data
+     */
+    public function process_course_completion_criteria($data) {
+        global $DB;
+
+        $data = (object)$data;
+        $data->course = $this->get_courseid();
+
+        // Apply the date offset to the time end field
+        $data->timeend = $this->apply_date_offset($data->timeend);
+
+        // Map the role from the criteria
+        if (!empty($data->role)) {
+            $data->role = $this->get_mappingid('role', $data->role);
+        }
+
+        // We backup the course shortname rather than the ID so that we can match back to the course
+        if (!empty($data->courseinstanceshortname)) {
+            $courseinstanceid = $DB->get_field('course', 'id', array('shortname'=>$data->courseinstanceshortname));
+            if (!$courseinstanceid) {
+                $courseinstanceid = null;
+            }
+        } else {
+            $courseinstanceid = null;
+        }
+        $data->courseinstance = $courseinstanceid;
+
+        // If the completion criteria is for a module we need to map the module instance
+        // to the new module id.
+        if (!empty($data->moduleinstance) && !empty($data->module)) {
+            $data->moduleinstance = $this->get_mappingid('course_module', $data->moduleinstance);
+        } else {
+            $data->module = null;
+            $data->moduleinstance = null;
+        }
+
+        $params = array(
+            'course'         => $data->course,
+            'criteriatype'   => $data->criteriatype,
+            'enrolperiod'    => $data->enrolperiod,
+            'courseinstance' => $data->courseinstance,
+            'module'         => $data->module,
+            'moduleinstance' => $data->moduleinstance,
+            'timeend'        => $data->timeend,
+            'gradepass'      => $data->gradepass,
+            'role'           => $data->role
+        );
+        $newid = $DB->insert_record('course_completion_criteria', $params);
+        $this->set_mapping('course_completion_criteria', $data->id, $newid);
+    }
+
+    /**
+     * Processes course compltion criteria complete records
+     *
+     * @global moodle_database $DB
+     * @param stdClass $data
+     */
+    public function process_course_completion_crit_compl($data) {
+        global $DB;
+
+        $data = (object)$data;
+
+        $data->criteriaid = $this->get_new_parentid('course_completion_criteria');
+        $data->course = $this->get_courseid();
+        $data->userid = $this->get_mappingid('user', $data->userid);
+
+        if (!empty($data->criteriaid) && !empty($data->userid)) {
+            $params = array(
+                'userid' => $data->userid,
+                'course' => $data->course,
+                'criteriaid' => $data->criteriaid,
+                'timecompleted' => $this->apply_date_offset($data->timecompleted)
+            );
+            if (isset($data->gradefinal)) {
+                $params['gradefinal'] = $data->gradefinal;
+            }
+            if (isset($data->unenroled)) {
+                $params['unenroled'] = $data->unenroled;
+            }
+            if (isset($data->deleted)) {
+                $params['deleted'] = $data->deleted;
+            }
+            $DB->insert_record('course_completion_crit_compl', $params);
+        }
+    }
+
+    /**
+     * Process course completions
+     *
+     * @global moodle_database $DB
+     * @param stdClass $data
+     */
+    public function process_course_completions($data) {
+        global $DB;
+
+        $data = (object)$data;
+
+        $data->course = $this->get_courseid();
+        $data->userid = $this->get_mappingid('user', $data->userid);
+
+        if (!empty($data->userid)) {
+            $params = array(
+                'userid' => $data->userid,
+                'course' => $data->course,
+                'deleted' => $data->deleted,
+                'timenotified' => $this->apply_date_offset($data->timenotified),
+                'timeenrolled' => $this->apply_date_offset($data->timeenrolled),
+                'timestarted' => $this->apply_date_offset($data->timestarted),
+                'timecompleted' => $this->apply_date_offset($data->timecompleted),
+                'reaggregate' => $data->reaggregate
+            );
+            $DB->insert_record('course_completions', $params);
+        }
+    }
+
+    /**
+     * Process course completion notification records.
+     *
+     * Note: As of Moodle 2.0 this table is not being used however it has been
+     * left in in the hopes that one day the functionality there will be completed
+     *
+     * @global moodle_database $DB
+     * @param stdClass $data
+     */
+    public function process_course_completion_notify($data) {
+        global $DB;
+
+        $data = (object)$data;
+
+        $data->course = $this->get_courseid();
+        if (!empty($data->role)) {
+            $data->role = $this->get_mappingid('role', $data->role);
+        }
+
+        $params = array(
+            'course' => $data->course,
+            'role' => $data->role,
+            'message' => $data->message,
+            'timesent' => $this->apply_date_offset($data->timesent),
+        );
+        $DB->insert_record('course_completion_notify', $params);
+    }
+
+    /**
+     * Process course completion aggregate methods
+     *
+     * @global moodle_database $DB
+     * @param stdClass $data
+     */
+    public function process_course_completion_aggr_methd($data) {
+        global $DB;
+
+        $data = (object)$data;
+
+        $data->course = $this->get_courseid();
+
+        $params = array(
+            'course' => $data->course,
+            'criteriatype' => $data->criteriatype,
+            'method' => $data->method,
+            'value' => $data->value,
+        );
+        $DB->insert_record('course_completion_aggr_methd', $params);
+    }
+
+}
+
+/**
  * This structure step restores the grade items associated with one activity
  * All the grade items are made child of the "course" grade item but the original
  * categoryid is saved as parentitemid in the backup_ids table, so, when restoring
Index: moodle/backup/util/plan/backup_plan.class.php
--- moodle/backup/util/plan/backup_plan.class.php Base (1.1)
+++ moodle/backup/util/plan/backup_plan.class.php Locally Modified (Based On 1.1)
@@ -31,6 +31,7 @@
 
     protected $controller; // The backup controller building/executing this plan
     protected $basepath;   // Fullpath to dir where backup is created
+    protected $excludingdactivities;
 
     /**
      * Constructor - instantiates one object of this class
@@ -67,6 +68,14 @@
         return $this->controller->get_logger();
     }
 
+    public function is_excluding_activities() {
+        return $this->excludingdactivities;
+    }
+
+    public function set_excluding_activities() {
+        $this->excludingdactivities = true;
+    }
+
     public function log($message, $level, $a = null, $depth = null, $display = false) {
         backup_helper::log($message, $level, $a, $depth, $display, $this->get_logger());
     }
Index: moodle/backup/util/plan/backup_task.class.php
--- moodle/backup/util/plan/backup_task.class.php Base (1.2)
+++ moodle/backup/util/plan/backup_task.class.php Locally Modified (Based On 1.2)
@@ -42,7 +42,11 @@
     public function get_backupid() {
         return $this->plan->get_backupid();
     }
+
+    public function is_excluding_activities() {
+        return $this->plan->is_excluding_activities();
 }
+}
 
 /*
  * Exception class used by all the @backup_task stuff
