restore MDL-23109 Added role mapping interface and implemented searchable course...
authorSam Hemelryk <sam@moodle.com>
Tue, 27 Jul 2010 06:58:39 +0000 (06:58 +0000)
committerSam Hemelryk <sam@moodle.com>
Tue, 27 Jul 2010 06:58:39 +0000 (06:58 +0000)
backup/controller/restore_controller.class.php
backup/util/dbops/restore_dbops.class.php
backup/util/includes/restore_includes.php
backup/util/ui/renderer.php
backup/util/ui/restore_moodleform.class.php
backup/util/ui/restore_ui.class.php
backup/util/ui/restore_ui_components.php [new file with mode: 0644]
backup/util/ui/restore_ui_stage.class.php
lang/en/backup.php
theme/base/style/core.css

index 9c7e96b..8e3535c 100644 (file)
@@ -312,6 +312,14 @@ class restore_controller extends backup implements loggable {
         return $this->plan->get_results();
     }
 
+    /**
+     * Returns true if the prechecks have been executed
+     * @return bool
+     */
+    public function precheck_executed() {
+        return (is_array($this->precheck));
+    }
+
     public function get_precheck_results() {
         if (!is_array($this->precheck)) {
             throw new restore_controller_exception('precheck_not_executed');
index da04aeb..12d652a 100644 (file)
@@ -888,6 +888,16 @@ abstract class restore_dbops {
         $rs->close();
     }
 
+    /**
+     * Creates a skeleton record within the database using the passed parameters
+     * and returns the new course id.
+     *
+     * @global moodle_database $DB
+     * @param string $fullname
+     * @param string $shortname
+     * @param int $categoryid
+     * @return int The new course id
+     */
     public static function create_new_course($fullname, $shortname, $categoryid) {
         global $DB;
         $category = $DB->get_record('course_categories', array('id'=>$categoryid), '*', MUST_EXIST);
@@ -904,8 +914,13 @@ abstract class restore_dbops {
         return $DB->insert_record('course', $course);   
     }
 
+    /**
+     * Deletes all of the content associated with the given course (courseid)
+     * @param int $courseid
+     * @return bool True for success
+     */
     public static function delete_course_content($courseid) {
-        return remove_course_contents($courseid);
+        return remove_course_contents($courseid, false);
     }
 }
 
index f7abc76..89b99b8 100644 (file)
@@ -80,6 +80,7 @@ require_once($CFG->dirroot . '/backup/util/ui/backup_ui_setting.class.php');
 require_once($CFG->dirroot . '/backup/util/ui/restore_ui_stage.class.php');
 require_once($CFG->dirroot . '/backup/util/ui/restore_ui.class.php');
 require_once($CFG->dirroot . '/backup/util/ui/restore_moodleform.class.php');
+require_once($CFG->dirroot . '/backup/util/ui/restore_ui_components.php');
 
 // And some moodle stuff too
 require_once ($CFG->dirroot . '/tag/lib.php');
index 88c928c..e16e376 100644 (file)
@@ -146,7 +146,7 @@ class core_backup_renderer extends plugin_renderer_base {
         return $html;
     }
 
-    public function course_selector(moodle_url $nextstageurl, $details, $categories, $courses, $currentcourse = null) {
+    public function course_selector(moodle_url $nextstageurl, $details, $categories, restore_course_search $courses=null, $currentcourse = null) {
         global $CFG;
         require_once($CFG->dirroot.'/course/lib.php');
 
@@ -162,36 +162,37 @@ class core_backup_renderer extends plugin_renderer_base {
         // Current course
         if (!empty($currentcourse)) {
             $html .= $form;
-            $form .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'targetid', 'value'=>$currentcourse));
+            $html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'targetid', 'value'=>$currentcourse));
             $html .= html_writer::start_tag('div', array('class'=>'bcs-current-course backup-section'));
             $html .= $this->output->heading(get_string('restoretocurrentcourse', 'backup'), 2, array('class'=>'header'));
-            $html .= $this->backup_detail_input(get_string('restoretocurrentcourseadding', 'backup'), 'radio', 'target', backup::TARGET_CURRENT_ADDING);
+            $html .= $this->backup_detail_input(get_string('restoretocurrentcourseadding', 'backup'), 'radio', 'target', backup::TARGET_CURRENT_ADDING, array('checked'=>'checked'));
             $html .= $this->backup_detail_input(get_string('restoretocurrentcoursedeleting', 'backup'), 'radio', 'target', backup::TARGET_CURRENT_DELETING);
             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
         }
 
-        if (count($categories) > 0) {
+        if ($categories->get_resultscount() > 0 || $categories->get_search() == '') {
             // New course
             $html .= $form;
             $html .= html_writer::start_tag('div', array('class'=>'bcs-new-course backup-section'));
             $html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class'=>'header'));
             $html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target', backup::TARGET_NEW_COURSE, array('checked'=>'checked'));
-            $html .= $this->backup_detail_select(get_string('coursecategory', 'backup'), 'targetid', $categories);
+            //$html .= $this->backup_detail_select(get_string('coursecategory', 'backup'), 'targetid', $categories);
+            $html .= $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
         }
 
-        if (count($courses) > 0) {
+        if ($courses->get_resultscount() > 0 || $courses->get_search() == '') {
             // Existing course
             $html .= $form;
             $html .= html_writer::start_tag('div', array('class'=>'bcs-existing-course backup-section'));
             $html .= $this->output->heading(get_string('restoretoexistingcourse', 'backup'), 2, array('class'=>'header'));
-            $html .= $this->backup_detail_input(get_string('restoretoexistingcourseadding', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_ADDING);
+            $html .= $this->backup_detail_input(get_string('restoretoexistingcourseadding', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_ADDING, array('checked'=>'checked'));
             $html .= $this->backup_detail_input(get_string('restoretoexistingcoursedeleting', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_DELETING);
-            $html .= $this->backup_detail_select(get_string('restoretocourse', 'backup'), 'targetid', $courses);
+            $html .= $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
             $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
@@ -211,12 +212,22 @@ class core_backup_renderer extends plugin_renderer_base {
         return $html;
     }
 
-    protected function backup_detail_input($label, $type, $name, $value, array $attributes=array()) {
-        return $this->backup_detail_pair($label, html_writer::empty_tag('input', $attributes+array('name'=>$name, 'type'=>$type, 'value'=>$value)));
+    protected function backup_detail_input($label, $type, $name, $value, array $attributes=array(), $description=null) {
+        if (!empty ($description)) {
+            $description = html_writer::tag('span', $description, array('class'=>'description'));
+        } else {
+            $description = '';
+        }
+        return $this->backup_detail_pair($label, html_writer::empty_tag('input', $attributes+array('name'=>$name, 'type'=>$type, 'value'=>$value)).$description);
     }
 
-    protected function backup_detail_select($label, $name, $options, $selected='', $nothing=false, array $attributes=array()) {
-        return $this->backup_detail_pair($label, html_writer::select($options, 'targetid', '', false, $attributes));
+    protected function backup_detail_select($label, $name, $options, $selected='', $nothing=false, array $attributes=array(), $description=null) {
+        if (!empty ($description)) {
+            $description = html_writer::tag('span', $description, array('class'=>'description'));
+        } else {
+            $description = '';
+        }
+        return $this->backup_detail_pair($label, html_writer::select($options, $name, $selected, false, $attributes).$description);
     }
 
     public function precheck_notices($results) {
@@ -234,6 +245,30 @@ class core_backup_renderer extends plugin_renderer_base {
         return $output.html_writer::end_tag('div');
     }
 
+    public function substage_buttons($haserrors) {
+        $output  = html_writer::start_tag('div', array('continuebutton'));
+        if (!$haserrors) {
+            $output .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue')));
+        }
+        $output .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'cancel', 'value'=>get_string('cancel')));
+        $output .= html_writer::end_tag('div');
+        return $output;
+    }
+
+    public function role_mappings($rolemappings, $roles) {
+        $roles[0] = get_string('none');
+        $output  = html_writer::start_tag('div', array('class'=>'restore-rolemappings'));
+        $output .= $this->output->heading(get_string('restorerolemappings', 'backup'), 2);
+        foreach ($rolemappings as $id=>$mapping) {
+            $label = $mapping->name;
+            $name = 'mapping'.$id;
+            $selected = $mapping->targetroleid;
+            $output .= $this->backup_detail_select($label, $name, $roles, $mapping->targetroleid, false, array(), $mapping->description);
+        }
+        $output .= html_writer::end_tag('div');
+        return $output;
+    }
+
     public function continue_button($url, $method='post') {
         if (!($url instanceof moodle_url)) {
             $url = new moodle_url($url);
@@ -296,7 +331,104 @@ class core_backup_renderer extends plugin_renderer_base {
         $html .= '</div>';
         return $html;
     }
-    
+
+    public function render_restore_course_search(restore_course_search $component) {
+        $url = $component->get_url();
+
+        $output = html_writer::start_tag('div', array('class' => 'restore-course-search'));
+        if ($component->get_totalcount() === 0) {
+            $output .= $this->output->notification(get_string('nomatchingcourses', 'backup'));
+            $output .= html_writer::end_tag('div');
+            return $output;
+        }
+
+        $output .= html_writer::tag('div', get_string('totalcoursesearchresults', 'backup', $component->get_totalcount()), array('class'=>'rcs-totalresults'));
+
+        $output .= html_writer::start_tag('div', array('class' => 'rcs-results'));
+        if ($component->get_totalpages()>1) {
+            $pagingbar = new paging_bar($component->get_totalcount(), $component->get_page(), $component->get_pagelimit(), new moodle_url($url, array('searchcourses'=>1)), restore_course_search::$VAR_PAGE);
+            $output .= $this->output->render($pagingbar);
+        }
+
+        $table = new html_table();
+        $table->head = array('', get_string('shortname'), get_string('fullname'));
+        $table->data = array();
+        foreach ($component->get_results() as $course) {
+            $row = new html_table_row();
+            $row->attributes['class'] = 'rcs-course';
+            if (!$course->visible) {
+                $row->attributes['class'] .= ' dimmed';
+            }
+            $row->cells = array(
+                html_writer::empty_tag('input', array('type'=>'radio', 'name'=>'targetid', 'value'=>$course->id)),
+                $course->shortname,
+                $course->fullname
+            );
+            $table->data[] = $row;
+        }
+        $output .= html_writer::table($table);
+        if (isset($pagingbar)) {
+            $output .= $this->output->render($pagingbar);
+        }
+        $output .= html_writer::end_tag('div');
+
+        $output .= html_writer::start_tag('div', array('class'=>'rcs-search'));
+        $output .= html_writer::empty_tag('input', array('type'=>'text', 'name'=>restore_course_search::$VAR_SEARCH, 'value'=>$component->get_search()));
+        $output .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'searchcourses', 'value'=>get_string('search')));
+        $output .= html_writer::end_tag('div');
+        
+        $output .= html_writer::end_tag('div');
+        return $output;
+    }
+
+    public function render_restore_category_search(restore_category_search $component) {
+        $url = $component->get_url();
+
+        $output = html_writer::start_tag('div', array('class' => 'restore-course-search'));
+        if ($component->get_totalcount() === 0) {
+            $output .= $this->output->notification(get_string('nomatchingcourses', 'backup'));
+            $output .= html_writer::end_tag('div');
+            return $output;
+        }
+
+        $output .= html_writer::tag('div', get_string('totalcategorysearchresults', 'backup', $component->get_totalcount()), array('class'=>'rcs-totalresults'));
+
+        $output .= html_writer::start_tag('div', array('class' => 'rcs-results'));
+        if ($component->get_totalpages()>1) {
+            $pagingbar = new paging_bar($component->get_totalcount(), $component->get_page(), $component->get_pagelimit(), new moodle_url($url, array('searchcourses'=>1)), restore_category_search::$VAR_PAGE);
+            $output .= $this->output->render($pagingbar);
+        }
+
+        $table = new html_table();
+        $table->head = array('', get_string('name'), get_string('description'));
+        $table->data = array();
+        foreach ($component->get_results() as $category) {
+            $row = new html_table_row();
+            $row->attributes['class'] = 'rcs-course';
+            if (!$category->visible) {
+                $row->attributes['class'] .= ' dimmed';
+            }
+            $row->cells = array(
+                html_writer::empty_tag('input', array('type'=>'radio', 'name'=>'targetid', 'value'=>$category->id)),
+                $category->name,
+                format_text($category->description, $category->descriptionformat)
+            );
+            $table->data[] = $row;
+        }
+        $output .= html_writer::table($table);
+        if (isset($pagingbar)) {
+            $output .= $this->output->render($pagingbar);
+        }
+        $output .= html_writer::end_tag('div');
+
+        $output .= html_writer::start_tag('div', array('class'=>'rcs-search'));
+        $output .= html_writer::empty_tag('input', array('type'=>'text', 'name'=>restore_category_search::$VAR_SEARCH, 'value'=>$component->get_search()));
+        $output .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'searchcourses', 'value'=>get_string('search')));
+        $output .= html_writer::end_tag('div');
+
+        $output .= html_writer::end_tag('div');
+        return $output;
+    }
 }
 /**
  * Data structure representing backup files viewer
index 760da5f..c8a1ed7 100644 (file)
@@ -1,6 +1,27 @@
 <?php
 
-require_once($CFG->dirroot.'/backup/util/ui/backup_moodleform.class.php');
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains the forms used by the restore stages
+ *
+ * @package   moodlecore
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 
 class restore_moodleform extends base_moodleform {
     public function __construct(restore_ui_stage $uistage, $action = null, $customdata = null, $method = 'post', $target = '', $attributes = null, $editable = true) {
index 056a0c3..a4f7261 100644 (file)
@@ -50,6 +50,10 @@ class restore_ui extends base_ui {
      */
     protected $stage = null;
 
+    /**
+     * String mappings to the above stages
+     * @var array
+     */
     public static $stages = array(
         restore_ui::STAGE_CONFIRM       => 'confirm',
         restore_ui::STAGE_DESTINATION   => 'destination',
@@ -102,11 +106,15 @@ class restore_ui extends base_ui {
         $this->controller->process_ui_event();
         return $processoutcome;
     }
-
+    /**
+     * Returns true if the stage is independent (not requiring a restore controller)
+     * @return bool
+     */
     public function is_independent() {
         return false;
     }
     /**
+     * Gets the unique ID associated with this UI
      * @return string
      */
     public function get_uniqueid() {
@@ -163,6 +171,13 @@ class restore_ui extends base_ui {
         }
         return $restoreid;
     }
+    /**
+     * Initialised the requested independent stage
+     *
+     * @param int $stage One of self::STAGE_*
+     * @param int $contextid
+     * @return restore_ui_stage_confirm|restore_ui_stage_destination
+     */
     final public static function engage_independent_stage($stage, $contextid) {
         if (!($stage & self::STAGE_CONFIRM + self::STAGE_DESTINATION)) {
             throw new restore_ui_exception('dependentstagerequested');
@@ -214,19 +229,31 @@ class restore_ui extends base_ui {
         }
         return $items;
     }
-
+    /**
+     * Gets the name of this UI
+     * @return string
+     */
     public function get_name() {
         return 'restore';
     }
-
+    /**
+     * Gets the first stage for this UI
+     * @return int STAGE_CONFIRM
+     */
     public function get_first_stage_id() {
         return self::STAGE_CONFIRM;
     }
-
+    /**
+     * Returns true if this stage has substages of which at least one needs to be displayed
+     * @return bool
+     */
     public function requires_substage() {
         return ($this->stage->has_sub_stages() && !$this->stage->process());
     }
-
+    /**
+     * Displays this stage
+     * @param core_backup_renderer $renderer
+     */
     public function display($renderer) {
         if ($this->progress < self::PROGRESS_SAVED) {
             throw new base_ui_exception('backupsavebeforedisplay');
diff --git a/backup/util/ui/restore_ui_components.php b/backup/util/ui/restore_ui_components.php
new file mode 100644 (file)
index 0000000..030fac8
--- /dev/null
@@ -0,0 +1,422 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains components used by the restore UI
+ *
+ * @package   moodlecore
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * A base class that can be used to build a specific search upon
+ */
+abstract class restore_search_base implements renderable {
+
+    /**
+     * The default values for this components params
+     */
+    const DEFAULT_PAGE = 0;
+    const DEFAULT_PAGELIMIT = 5;
+    const DEFAULT_SEARCH = '';
+
+    /**
+     * The param used to convey the current page
+     * @var string
+     */
+    static $VAR_PAGE = 'page';
+    /**
+     * The param used to convey the current page limit
+     * @var string
+     */
+    static $VAR_PAGELIMIT = 'pagelimit';
+    /**
+     * The param used to convey the current search string
+     * @var string
+     */
+    static $VAR_SEARCH = 'search';
+    /**
+     * The current search string
+     * @var string|null
+     */
+    private $search = null;
+    /**
+     * The current page
+     * @var int
+     */
+    private $page = null;
+    /**
+     * The current page limit
+     * @var int
+     */
+    private $pagelimit = null;
+    /**
+     * The URL for this page including required params to return to it
+     * @var moodle_url
+     */
+    private $url = null;
+    /**
+     * The results of the search
+     * @var array|null
+     */
+    private $results = null;
+    /**
+     * The total number of results available
+     * @var int
+     */
+    private $totalcount = null;
+    /**
+     * Array of capabilities required for each item in the search
+     * @var array
+     */
+    private $requiredcapabilities = array();
+
+    /**
+     * Constructor
+     * @param array $config Config options
+     */
+    public function __construct(array $config=array()) {
+
+        $this->page = optional_param($this->get_varpage(), self::DEFAULT_PAGE, PARAM_INT);
+        $this->pagelimit = optional_param($this->get_varpagelimit(), self::DEFAULT_PAGELIMIT, PARAM_INT);
+        $this->search = optional_param($this->get_varsearch(), self::DEFAULT_SEARCH, PARAM_ALPHANUMEXT);
+
+        foreach ($config as $name=>$value) {
+            $method = 'set_'.$name;
+            if (method_exists($this, $method)) {
+                $this->$method($value);
+            }
+        }
+    }
+    /**
+     * The current page
+     * @return int
+     */
+    final public function get_page() {
+        return ($this->page !== null)?$this->page:self::DEFAULT_PAGE;
+    }
+    /**
+     * The current page limit
+     * @return int
+     */
+    final public function get_pagelimit() {
+        return ($this->pagelimit !== null)?$this->pagelimit:self::DEFAULT_PAGELIMIT;
+    }
+    /**
+     * The URL for this search
+     * @global moodle_page $PAGE
+     * @return moodle_url The URL for this page
+     */
+    final public function get_url() {
+        global $PAGE;
+        $params = array(
+            $this->get_varpage()      => $this->get_page(),
+            $this->get_varpagelimit() => $this->get_pagelimit(),
+            $this->get_varsearch()    => $this->get_search()
+        );
+        return ($this->url !== null)?new moodle_url($this->url, $params):new moodle_url($PAGE->url, $params);
+    }
+    /**
+     * The current search string
+     * @return string
+     */
+    final public function get_search() {
+        return ($this->search !== null)?$this->search:self::DEFAULT_SEARCH;
+    }
+    /**
+     * The total number of results
+     * @return int
+     */
+    final public function get_totalcount() {
+        if ($this->totalcount === null) {
+            $this->search();
+        }
+        return $this->totalcount;
+    }
+    /**
+     * The number of results in this result set
+     * @return int
+     */
+    final public function get_resultscount() {
+        if ($this->results === null) {
+            $this->search();
+        }
+        return count($this->results);
+    }
+    /**
+     * Returns an array of results from the search
+     * @return array
+     */
+    final public function get_results() {
+        if ($this->results === null) {
+            $this->search();
+        }
+        return $this->results;
+    }
+    /**
+     * Sets the current page
+     * @param int $page
+     */
+    final public function set_page($page) {
+        $this->page = abs((int)$page);
+        $this->invalidate_results();
+    }
+    /**
+     * Sets the page limit for this component
+     * @param int $pagelimit
+     */
+    final public function set_pagelimit($pagelimit) {
+        $this->pagelimit = abs((int)$pagelimit);
+        if ($this->pagelimit < 5 || $this->pagelimit > 500) {
+            $this->pagelimit = null;
+        }
+        $this->invalidate_results();
+    }
+    /**
+     * Returns the total number of pages
+     * @return int
+     */
+    final public function get_totalpages() {
+        return ceil($this->get_totalcount()/$this->pagelimit);
+    }
+    /**
+     * Sets the page URL
+     * @param moodle_url $url
+     */
+    final public function set_url(moodle_url $url) {
+        $this->url = $url;
+    }
+    /**
+     * Invalidates the results collected so far
+     */
+    final public function invalidate_results() {
+        $this->results = null;
+    }
+    /**
+     * Adds a required capability which all results will be checked against
+     * @param string $capability
+     * @param int|null $user
+     */
+    final public function require_capability($capability, $user=null) {
+        if (!is_int($user)) {
+            $user = null;
+        }
+        $this->requiredcapabilities[] = array(
+            'capability' => $capability,
+            'user' => $user
+        );
+    }
+    /**
+     * Executes the search
+     *
+     * @global moodle_database $DB
+     * @return int The number of results
+     */
+    final public function search() {
+        global $DB;
+        if (!is_null($this->results)) {
+            return $this->results;
+        }
+        $params = array(
+            2=>$this->get_page()*$this->get_pagelimit(),
+            3=>$this->get_pagelimit()
+        );
+        $this->results = call_user_func_array(array($DB, 'get_records_sql'), $this->get_searchsql()+$params);
+        $this->totalcount = call_user_func_array(array($DB, 'count_records_sql'), $this->get_countsql()+$params);
+
+        if (count($this->requiredcapabilities) > 0) {
+            $contextlevel = $this->get_itemcontextlevel();
+            foreach ($this->results as $key=>$result) {
+                context_instance_preload($result);
+                $context = get_context_instance($contextlevel, $result->id);
+                foreach ($this->requiredcapabilities as $cap) {
+                    if (!has_capability($cap['capability'], $context, $cap['user'])) {
+                        unset($this->results[$key]);
+                        break 1;
+                    }
+                }
+            }
+        }
+
+        $this->format_results();
+
+        return $this->get_resultscount();
+    }
+    /**
+     * Returns an array containing the SQL for the search and the params
+     * @return array
+     */
+    abstract protected function get_searchsql();
+    /**
+     * Returns an array containing the SQL to get the total number of search results
+     * as well as an array of params for it
+     * @return array
+     */
+    abstract protected function get_countsql();
+    /**
+     * Gets the context level associated with this components items
+     * @return CONTEXT_*
+     */
+    abstract protected function get_itemcontextlevel();
+    /**
+     * Formats the results
+     */
+    abstract protected function format_results();
+    /**
+     * Gets the string used to transfer the page for this compontents requests
+     * @return string
+     */
+    abstract public function get_varpage();
+    /**
+     * Gets the string used to transfer the page limit for this compontents requests
+     * @return string
+     */
+    abstract public function get_varpagelimit();
+    /**
+     * Gets the string used to transfer the search string for this compontents requests
+     * @return string
+     */
+    abstract public function get_varsearch();
+}
+
+/**
+ * A course search component
+ */
+class restore_course_search extends restore_search_base {
+
+    static $VAR_PAGE = 'page';
+    static $VAR_PAGELIMIT = 'pagelimit';
+    static $VAR_SEARCH = 'search';
+
+    public function __construct(array $config=array()) {
+        parent::__construct($config);
+        $this->require_capability('moodle/restore:restorecourse');
+    }
+    /**
+     *
+     * @global moodle_database $DB
+     */
+    protected function get_searchsql() {
+        global $DB;
+
+        list($ctxselect, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+        $like = $DB->sql_ilike();
+        $params = array(
+            'fullnamesearch' => $this->get_search().'%',
+            'shortnamesearch' => '%'.$this->get_search().'%'
+        );
+
+        $select     = " SELECT c.id,c.fullname,c.shortname,c.visible,c.sortorder ";
+        $from       = " FROM {course} c ";
+        $where      = " WHERE c.fullname $like :fullnamesearch OR c.shortname $like :shortnamesearch ";
+        $orderby    = " ORDER BY c.sortorder";
+
+        return array($select.$ctxselect.$from.$ctxjoin.$where.$orderby, $params);
+    }
+    protected function get_countsql() {
+        global $DB;
+        
+        $like = $DB->sql_ilike();
+        $params = array(
+            'fullnamesearch' => $this->get_search().'%',
+            'shortnamesearch' => '%'.$this->get_search().'%'
+        );
+
+        $select     = " SELECT COUNT(c.id) ";
+        $from       = " FROM {course} c ";
+        $where      = " WHERE c.fullname $like :fullnamesearch OR c.shortname $like :shortnamesearch ";
+
+        return array($select.$from.$where, $params);
+    }
+    protected function get_itemcontextlevel() {
+        return CONTEXT_COURSE;
+    }
+    protected function format_results() {}
+    public function get_varpage() {
+        return self::$VAR_PAGE;
+    }
+    public function get_varpagelimit() {
+        return self::$VAR_PAGELIMIT;
+    }
+    public function get_varsearch() {
+        return self::$VAR_SEARCH;
+    }
+}
+
+/**
+ * A category search component
+ */
+class restore_category_search extends restore_search_base  {
+
+    static $VAR_PAGE = 'catpage';
+    static $VAR_PAGELIMIT = 'catpagelimit';
+    static $VAR_SEARCH = 'catsearch';
+
+    public function __construct(array $config=array()) {
+        parent::__construct($config);
+        $this->require_capability('moodle/course:create');
+    }
+    /**
+     *
+     * @global moodle_database $DB
+     */
+    protected function get_searchsql() {
+        global $DB;
+
+        list($ctxselect, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx');
+        $like = $DB->sql_ilike();
+        $params = array(
+            'namesearch' => $this->get_search().'%',
+        );
+
+        $select     = " SELECT c.id,c.name,c.visible,c.sortorder,c.description,c.descriptionformat ";
+        $from       = " FROM {course_categories} c ";
+        $where      = " WHERE c.name $like :namesearch ";
+        $orderby    = " ORDER BY c.sortorder";
+
+        return array($select.$ctxselect.$from.$ctxjoin.$where.$orderby, $params);
+    }
+    protected function get_countsql() {
+        global $DB;
+
+        $like = $DB->sql_ilike();
+        $params = array(
+            'namesearch' => $this->get_search().'%',
+        );
+
+        $select     = " SELECT COUNT(c.id) ";
+        $from       = " FROM {course_categories} c ";
+        $where      = " WHERE c.name $like :namesearch ";
+
+        return array($select.$from.$where, $params);
+    }
+    protected function get_itemcontextlevel() {
+        return CONTEXT_COURSECAT;
+    }
+    protected function format_results() {}
+    public function get_varpage() {
+        return self::$VAR_PAGE;
+    }
+    public function get_varpagelimit() {
+        return self::$VAR_PAGELIMIT;
+    }
+    public function get_varsearch() {
+        return self::$VAR_SEARCH;
+    }
+}
+
index 3d7fab5..1816b4d 100644 (file)
@@ -41,7 +41,7 @@
  */
 abstract class restore_ui_stage extends base_ui_stage {
     /**
-     *
+     * Constructor
      * @param restore_ui $ui
      */
     public function __construct(restore_ui $ui, array $params=null) {
@@ -55,11 +55,18 @@ abstract class restore_ui_stage extends base_ui_stage {
     final public function get_restoreid() {
         return $this->get_uniqueid();
     }
-
+    /**
+     * This is an independent stage
+     * @return int
+     */
     final public function is_independent() {
         return false;
     }
 
+    /**
+     * No sub stages for this stage
+     * @return false
+     */
     public function has_sub_stages() {
         return false;
     }
@@ -71,11 +78,21 @@ abstract class restore_ui_stage extends base_ui_stage {
     final public function get_name() {
         return get_string('restorestage'.$this->stage,'backup');
     }
+    /**
+     * Returns true if this is the settings stage
+     * @return bool
+     */
     final public function is_first_stage() {
         return $this->stage == restore_ui::STAGE_SETTINGS;
     }
 }
 
+/**
+ * Abstract class used to represent a restore stage that is indenependent.
+ *
+ * An independent stage is a judged to be so because it doesn't require, and has
+ * no use for the restore controller.
+ */
 abstract class restore_ui_independent_stage {
     abstract public function __construct($contextid);
     abstract public function process();
@@ -109,11 +126,20 @@ abstract class restore_ui_independent_stage {
         return $items;
     }
     abstract public function get_stage_name();
+    /**
+     * Obviously true
+     * @return true
+     */
     final public function is_independent() {
         return true;
     }
 }
 
+/**
+ * The confirmation stage.
+ *
+ * This is the first stage, it is independent.
+ */
 class restore_ui_stage_confirm extends restore_ui_independent_stage {
     protected $contextid;
     protected $filename = null;
@@ -154,21 +180,38 @@ class restore_ui_stage_confirm extends restore_ui_independent_stage {
     }
 }
 
+/**
+ * This is the destination stage.
+ *
+ * This stage is the second stage and is also independent
+ */
 class restore_ui_stage_destination extends restore_ui_independent_stage {
     protected $contextid;
     protected $filepath = null;
     protected $details;
     protected $courseid = null;
     protected $target = backup::TARGET_NEW_COURSE;
+    protected $coursesearch = null;
+    protected $categorysearch = null;
     public function __construct($contextid) {
+        global $PAGE;
         $this->contextid = $contextid;
         $this->filepath = required_param('filepath', PARAM_ALPHANUM);
+        $url = new moodle_url($PAGE->url, array(
+            'filepath'=>$this->filepath,
+            'contextid'=>$this->contextid,
+            'stage'=>restore_ui::STAGE_DESTINATION));
+        $this->coursesearch = new restore_course_search(array('url'=>$url));
+        $this->categorysearch = new restore_category_search(array('url'=>$url));
     }
     public function process() {
         global $CFG, $DB;
         if (!file_exists("$CFG->dataroot/temp/backup/".$this->filepath) || !is_dir("$CFG->dataroot/temp/backup/".$this->filepath)) {
             throw new restore_ui_exception('invalidrestorepath');
         }
+        if (optional_param('searchcourses', false, PARAM_BOOL)) {
+            return false;
+        }
         $this->target = optional_param('target', backup::TARGET_NEW_COURSE, PARAM_INT);
         $targetid = optional_param('targetid', null, PARAM_INT);
         if (!is_null($this->target) && !is_null($targetid) && confirm_sesskey()) {
@@ -189,27 +232,14 @@ class restore_ui_stage_destination extends restore_ui_independent_stage {
      * @return string
      */
     public function display($renderer) {
-        global $DB, $USER;
+        global $DB, $USER, $PAGE;
         $this->details = backup_general_helper::get_backup_information($this->filepath);
         $url = new moodle_url('/backup/restore.php', array('contextid'=>$this->contextid, 'filepath'=>$this->filepath, 'stage'=>restore_ui::STAGE_SETTINGS));
         
         $context = get_context_instance_by_id($this->contextid);
         $currentcourse = ($context->contextlevel == CONTEXT_COURSE && has_capability('moodle/restore:restorecourse', $context))?$context->instanceid:false;
-        
-        $courselist = array();
-        $courses = get_user_capability_course('moodle/restore:restorecourse', $USER->id, true, 'fullname,shortname,visible,sortorder');
-        foreach ($courses as $course) {
-            $courselist[$course->id] = $course->fullname.' ['.$course->shortname.']';
-        }
-
-        $categories = make_categories_options();
-        foreach ($categories as $categoryid=>$category) {
-            if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $categoryid))) {
-                unset($categories[$categoryid]);
-            }
-        }
 
-        $html = $renderer->course_selector($url, $this->details, $categories, $courselist, $currentcourse);
+        $html = $renderer->course_selector($url, $this->details, $this->categorysearch, $this->coursesearch, $currentcourse);
         return $html;
     }
     public function get_stage_name() {
@@ -228,7 +258,12 @@ class restore_ui_stage_destination extends restore_ui_independent_stage {
         return $this->target;
     }
 }
-
+/**
+ * This stage is the settings stage.
+ *
+ * This stage is the third stage, it is dependent on a restore controller and
+ * is the first stage as such.
+ */
 class restore_ui_stage_settings extends restore_ui_stage {
     /**
      * Initial restore stage constructor
@@ -535,6 +570,11 @@ class restore_ui_stage_process extends restore_ui_stage {
      * of the completed stage.
      */
     public function process(base_moodleform $form=null) {
+        if (optional_param('cancel', false, PARAM_BOOL)) {
+            redirect(new moodle_url('/course/view.php', array('id'=>$this->get_ui()->get_controller()->get_courseid())));
+        }
+
+        // First decide whether a substage is needed
         $rc = $this->ui->get_controller();
         if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
             $this->substage = self::SUBSTAGE_CONVERT;
@@ -542,10 +582,35 @@ class restore_ui_stage_process extends restore_ui_stage {
             if ($rc->get_status() == backup::STATUS_SETTING_UI) {
                 $rc->finish_ui();
             }
-            if ($rc->get_status() == backup::STATUS_NEED_PRECHECK && !$rc->execute_precheck(true)) {
-                $this->substage = self::SUBSTAGE_PRECHECKS;
+            if ($rc->get_status() == backup::STATUS_NEED_PRECHECK) {
+                if (!$rc->precheck_executed()) {
+                    $rc->execute_precheck(true);
+                }
+                $results = $rc->get_precheck_results();
+                if (!empty($results)) {
+                    $this->substage = self::SUBSTAGE_PRECHECKS;
+                }
             }
         }
+
+        $substage = optional_param('substage', null, PARAM_INT);
+        if (empty($this->substage) && !empty($substage)) {
+            $this->substage = $substage;
+            // Now check whether that substage has already been submit
+            if ($this->substage == self::SUBSTAGE_PRECHECKS && optional_param('sesskey', null, PARAM_RAW) == sesskey()) {
+                $info = $rc->get_info();
+                if (!empty($info->role_mappings->mappings)) {
+                    foreach ($info->role_mappings->mappings as $key=>&$mapping) {
+                        $mapping->targetroleid = optional_param('mapping'.$key, $mapping->targetroleid, PARAM_INT);
+                    }
+                    $info->role_mappings->modified = true;
+                }
+                // We've processed the substage now setting it back to none so we
+                // can move to the next stage.
+                $this->substage = self::SUBSTAGE_NONE;
+            }
+        }
+
         return empty($this->substage);
     }
     /**
@@ -559,18 +624,32 @@ class restore_ui_stage_process extends restore_ui_stage {
      */
     public function display($renderer) {
         global $PAGE;
+        $haserrors = false;
+        $url = new moodle_url($PAGE->url, array('restore'=>$this->get_uniqueid(), 'stage'=>restore_ui::STAGE_PROCESS, 'substage'=>$this->substage, 'sesskey'=>sesskey()));
+        echo html_writer::start_tag('form', array('action'=>$url->out_omit_querystring(), 'class'=>'backup-restore', 'method'=>'post'));
+        foreach ($url->params() as $name=>$value) {
+            echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>$name, 'value'=>$value));
+        }
         switch ($this->substage) {
             case self::SUBSTAGE_CONVERT :
                 echo '<h2>Need to show the conversion screens here</h2>';
                 break;
             case self::SUBSTAGE_PRECHECKS :
                 $results = $this->ui->get_controller()->get_precheck_results();
+                $info = $this->ui->get_controller()->get_info();
+                $haserrors = (!empty($results['errors']));
                 echo $renderer->precheck_notices($results);
+                if (!empty($info->role_mappings->mappings)) {
+                    $context = get_context_instance(CONTEXT_COURSE, $this->ui->get_controller()->get_courseid());
+                    $assignableroles = get_assignable_roles($context, ROLENAME_ALIAS, false);
+                    echo $renderer->role_mappings($info->role_mappings->mappings, $assignableroles);
+                }
                 break;
             default:
                 throw new restore_ui_exception('backup_ui_must_execute_first');
         }
-        echo $renderer->continue_button(new moodle_url($PAGE->url, array('restore'=>$this->get_uniqueid(), 'stage'=>restore_ui::STAGE_PROCESS, 'substage'=>$this->substage)));
+        echo $renderer->substage_buttons($haserrors);
+        echo html_writer::end_tag('form');
     }
 
     public function has_sub_stages() {
@@ -578,6 +657,11 @@ class restore_ui_stage_process extends restore_ui_stage {
     }
 }
 
+/**
+ * This is the completed stage.
+ *
+ * Once this is displayed there is nothing more to do.
+ */
 class restore_ui_stage_complete extends restore_ui_stage_process {
     /**
      * The results of the backup execution
index f70b10d..5664c4a 100644 (file)
@@ -96,6 +96,7 @@ $string['lockedbypermission'] = 'You don\'t have sufficient permissions to chang
 $string['lockedbyconfig'] = 'This setting has been locked by the default backup settings';
 $string['lockedbyhierarchy'] = 'Locked by dependencies';
 $string['moodleversion'] = 'Moodle version';
+$string['nomatchingcourses'] = 'There are no courses to display';
 $string['originalwwwroot'] = 'URL of backup';
 $string['previousstage'] = 'Previous';
 $string['restoreactivity'] = 'Restore activity';
@@ -132,12 +133,17 @@ $string['restoretoexistingcoursedeleting'] = 'Delete the contents of the existin
 $string['restoretonewcourse'] = 'Restore as a new course';
 $string['restoringcourse'] = 'Course restoration in progress';
 $string['restoringcourseshortname'] = 'restoring';
+$string['restorerolemappings'] = 'Restore role mappings';
 $string['rootsettings'] = 'Backup settings';
 $string['scheduledsettings'] = 'Scheduled backup settings';
 $string['sectionincanduser'] = 'Included in backup along with user information';
 $string['sectioninc'] = 'Included in backup (no user information)';
 $string['sectionactivities'] = 'Activities';
+$string['selectacategory'] = 'Select a category';
+$string['selectacourse'] = 'Select a course';
 $string['setting_overwriteconf'] = 'Overwrite course configuration';
 $string['setting_course_fullname'] = 'Course name';
 $string['setting_course_shortname'] = 'Course short name';
 $string['setting_course_startdate'] = 'Course startdate';
+$string['totalcategorysearchresults'] = 'Total categories: {$a}';
+$string['totalcoursesearchresults'] = 'Total courses: {$a}';
\ No newline at end of file
index 88b6634..12faa5c 100644 (file)
@@ -656,6 +656,14 @@ body.tag .managelink {padding: 5px;}
 .backup-restore .detail-pair-value {display:inline-block;width:65%;padding:8px;margin:0;}
 .backup-restore .detail-pair-value > .sub-detail {display:block;color:#1580B6;margin-left:2em;font-size:90%;font-style: italic;}
 
+.restore-course-search .rcs-results {width:70%;min-width:400px;border:1px solid #ddd;margin:5px 0;}
+.restore-course-search .rcs-results table {width:100%;margin:0;border-width:0;}
+.restore-course-search .rcs-results .paging {text-align:left;margin:0;background-color:#eee;padding:3px;}
+
+.restore-course-category .rcs-results {width:70%;min-width:400px;border:1px solid #ddd;margin:5px 0;}
+.restore-course-category .rcs-results table {width:100%;margin:0;border-width:0;}
+.restore-course-category .rcs-results .paging {text-align:left;margin:0;background-color:#eee;padding:3px;}
+
 .corelightbox {background-color:#CCC;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;}
 .corelightbox img {position:fixed;top:50%;}