Index: backup/restorefile.php
=========================================================
--- backup/restorefile.php	Thu Jul 08 11:50:16 WST 2010
+++ backup/restorefile.php	Thu Jul 08 11:50:16 WST 2010
@@ -0,0 +1,104 @@
+<?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/>.
+
+/**
+ * Import backup file or select existing backup file from moodle
+ * @package   moodlecore
+ * @copyright 2010 Dongsheng Cai <dongsheng@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once(dirname(__FILE__).'/restorefile_form.php');
+
+$contextid = required_param('contextid', PARAM_INT);
+// action
+$action = optional_param('action', '', PARAM_ALPHA);
+// file parameters
+// non js interface may require these parameters
+$component  = optional_param('component', null, PARAM_ALPHAEXT);
+$filearea   = optional_param('filearea', null, PARAM_ALPHAEXT);
+$itemid     = optional_param('itemid', null, PARAM_INT);
+$filepath   = optional_param('filepath', null, PARAM_PATH);
+$filename   = optional_param('filename', null, PARAM_FILE);
+
+list($context, $course, $cm) = get_context_info_array($contextid);
+
+$url = new moodle_url('/backup/restorefile.php', array('contextid'=>$contextid));
+
+switch ($context->contextlevel) {
+    case CONTEXT_COURSE:
+        $heading = get_string('restorecourse', 'backup');
+        break;
+    case CONTEXT_MODULE:
+        $heading = get_string('restoreactivity', 'backup');
+        break;
+    // TODO
+}
+
+
+require_login($course);
+require_capability('moodle/restore:restorecourse', $context);
+
+$PAGE->set_url($url);
+$PAGE->set_context($context);
+$PAGE->set_title(get_string('course') . ': ' . $course->fullname);
+$PAGE->set_heading($heading);
+$PAGE->set_pagelayout('admin');
+
+// choose the backup file from backup files tree
+if ($action == 'choosebackupfile') {
+    $browser = get_file_browser();
+    if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
+        // TODO: filename should be generated by restore_controller::get_tempdir_name()
+        $filename = 'randomname.zip';
+        $pathname = "$CFG->dataroot/temp/backup/".$filename;
+        $fileinfo->copy_to_pathname($pathname);
+        $restore_url = new moodle_url('/backup/restore.php', array('contextid'=>$contextid, 'filename'=>$filename));
+        redirect($restore_url);
+    } else {
+        redirect($url, get_string('filenotfound', 'error'));
+    }
+    die;
+}
+
+$form = new course_restore_form(null, array('contextid'=>$contextid));
+$data = $form->get_data();
+if ($data) {
+    // TODO: filename should be generated by restore_controller::get_tempdir_name()
+    $filename = 'randomname2.zip';
+    $pathname = "$CFG->dataroot/temp/backup/".$filename;
+    $form->save_file('backupfile', $pathname);
+    $restore_url = new moodle_url('/backup/restore.php', array('contextid'=>$contextid, 'filename'=>$filename));
+    redirect($restore_url);
+    die;
+}
+
+$browser = get_file_browser();
+$fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename);
+
+echo $OUTPUT->header();
+echo $OUTPUT->container_start();
+$output = $PAGE->get_renderer('core', 'backup');
+echo $output->backup_files_viewer($fileinfo, array());
+echo $OUTPUT->container_end();
+
+echo $OUTPUT->container_start();
+$form->display();
+echo $OUTPUT->container_end();
+
+echo $OUTPUT->footer();
Index: backup/restorefile_form.php
=========================================================
--- backup/restorefile_form.php	Thu Jul 08 11:45:12 WST 2010
+++ backup/restorefile_form.php	Thu Jul 08 11:45:12 WST 2010
@@ -0,0 +1,34 @@
+<?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/>.
+
+/**
+ * Import backup file form
+ * @package   moodlecore
+ * @copyright 2010 Dongsheng Cai <dongsheng@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+class course_restore_form extends moodleform {
+    function definition() {
+        $mform =& $this->_form;
+        $contextid = $this->_customdata['contextid'];
+        $mform->addElement('hidden', 'contextid', $contextid);
+        $mform->addElement('filepicker', 'backupfile', get_string('files'));
+        $submit_string = get_string('restore');
+        $this->add_action_buttons(false, $submit_string);
+    }
+}
Index: backup/util/ui/module.js
=========================================================
--- backup/util/ui/module.js	Thu Jul 08 11:58:27 WST 2010
+++ backup/util/ui/module.js	Thu Jul 08 11:58:27 WST 2010
@@ -0,0 +1,156 @@
+// backup files tree
+// Author: Dongsheng Cai <dongsheng@moodle.com>
+M.core_backup_files_tree = {
+    y3: null,
+    api: M.cfg.wwwroot+'/files/filebrowser_ajax.php',
+    request: function(url, node, cb) {
+        var api = this.api + '?action=getfiletree';
+        var params = [];
+        params['contextid'] = this.get_param(url, 'contextid', -1);
+        params['component'] = this.get_param(url, 'component', null);
+        params['filearea']  = this.get_param(url, 'filearea', null);
+        params['itemid']    = this.get_param(url, 'itemid', -1);
+        params['filepath']  = this.get_param(url, 'filepath', null);
+        params['filename']  = this.get_param(url, 'filename', null);
+        var scope = this;
+        params['sesskey']=M.cfg.sesskey;
+        var cfg = {
+            method: 'POST',
+            on: {
+                complete: function(id,o,p) {
+                    try {
+                        var data = this.y3.JSON.parse(o.responseText);
+                    } catch(e) {
+                        alert(e.toString());
+                        return;
+                    }
+                    if (data && data.length==0) {
+                        node.isLeaf = true;
+                    } else {
+                        for (i in data) {
+                            if (data[i].isdir) {
+                                var info = {label: data[i].filename, href: data[i].url};
+                                var n = new YAHOO.widget.TextNode(info, node, false);
+                                YAHOO.util.Event.addListener(n.labelElId, "click", function(e) {
+                                    YAHOO.util.Event.preventDefault(e);
+                                }); 
+                                n.isLeaf = false;
+                            } else {
+                                var params = data[i].params;
+                                params.action = 'choosebackupfile';
+                                var restoreurl = M.cfg.wwwroot+'/backup/restorefile.php?'+build_querystring(params);
+                                var info = {label: data[i].filename, 'href': data[i].url, 'restoreurl': restoreurl};
+                                params['filename'] = data[i].filename;
+                                var n = new YAHOO.widget.RestoreNode(info, node, false);
+                                n.isLeaf = true;
+                            }
+                        }
+                    }
+                    cb();
+                }
+            },
+            arguments: {
+                scope: scope
+            },
+            headers: {
+                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
+            },
+            data: build_querystring(params),
+            context: this
+        };
+        this.y3.io(api, cfg);
+    },
+    init : function(Y, htmlid){
+        var tree = new YAHOO.widget.TreeView(htmlid);
+        tree.setDynamicLoad(this.dynload);
+        var root = tree.getRoot();
+        var children = root.children;
+        tree.subscribe("clickEvent", function(e) {
+            if(!e.node.isLeaf){
+                e.node.toggle();
+            }
+        });
+        for (i in children) {
+            var node = children[i];
+            if (node.className == 'file-tree-folder') {
+                node.isLeaf = false;
+                // prevent link
+                YAHOO.util.Event.addListener(node.labelElId, "click", function(e) {
+                    YAHOO.util.Event.preventDefault(e);
+                }); 
+            } else {
+                node.isLeaf = true;
+            }
+        }
+        tree.render();
+        this.y3 = Y;
+    }, 
+    dynload: function(node, oncompletecb) {
+        M.core_backup_files_tree.request(node.href, node, oncompletecb);
+    },
+    get_param: function(url, name, val) {
+        name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+        var regexS = "[\\?&]"+name+"=([^&#]*)";
+        var regex = new RegExp( regexS );
+        var results = regex.exec(url);
+        if( results == null ) {
+            return val;
+        } else {
+            return unescape(results[1]);
+        }
+    }
+}
+
+YAHOO.widget.RestoreNode = function(oData, oParent, expanded) {
+
+    if (oData) { 
+        if (YAHOO.lang.isString(oData)) {
+            oData = { label: oData };
+        }
+        this.init(oData, oParent, expanded);
+        this.setUpLabel(oData);
+    }
+
+};
+YAHOO.extend(YAHOO.widget.RestoreNode, YAHOO.widget.TextNode, {
+    labelStyle: "ygtvlabel",
+    labelElId: null,
+    label: null,
+    title: null,
+    href: null,
+    target: "_blank",
+    _type: "RestoreNode",
+    setUpLabel: function(oData) { 
+        if (YAHOO.lang.isString(oData)) {
+            oData = { 
+                label: oData 
+            };
+        } else {
+            if (oData.style) {
+                this.labelStyle = oData.style;
+            }
+        }
+
+        this.label = oData.label;
+        this.restoreurl = oData.restoreurl;
+        this.labelElId = "ygtvlabelel" + this.index;
+    },
+    getContentHtml: function() { 
+        var sb = [];
+        sb[sb.length] = '<a';
+        sb[sb.length] = ' id="' + this.labelElId + '"';
+        sb[sb.length] = ' class="' + this.labelStyle  + '"';
+        if (this.href) {
+            sb[sb.length] = ' href="' + this.href + '"';
+            sb[sb.length] = ' target="' + this.target + '"';
+        } 
+        if (this.title) {
+            sb[sb.length] = ' title="' + this.title + '"';
+        }
+        sb[sb.length] = ' >';
+        sb[sb.length] = this.label;
+        sb[sb.length] = '</a>';
+        sb[sb.length] = ' <a href="'+this.restoreurl+'">'+M.str.moodle.restore+'</a>';
+        return sb.join("");
+    }
+});
Index: backup/util/ui/renderer.php
=========================================================
--- backup/util/ui/renderer.php	(revision 1.2)
+++ backup/util/ui/renderer.php	Thu Jul 08 11:49:38 WST 2010
@@ -63,4 +63,124 @@
     public function dependency_notification($message) {
         return html_writer::tag('div', $message, array('class'=>'notification dependencies_enforced'));
     }
-}
\ No newline at end of file
+    /**
+     * Print a backup files tree
+     * @param file_info $fileinfo
+     * @param array $options
+     * @return string
+     */
+    public function backup_files_viewer(file_info $fileinfo, array $options = null) {
+        $tree = new backup_files_viewer($fileinfo, $options);
+        return $this->render($tree);
+    }
+
+    public function render_backup_files_viewer(backup_files_viewer $tree) {
+        $module = array('name'=>'backup_files_tree', 'fullpath'=>'/backup/util/ui/module.js', 'requires'=>array('yui2-treeview', 'yui2-json'), 'strings'=>array(array('restore', 'moodle')));
+        $htmlid = 'backup-treeview-'.uniqid();
+        $this->page->requires->js_init_call('M.core_backup_files_tree.init', array($htmlid), false, $module);
+
+        $html = '<div>';
+        foreach($tree->path as $path) {
+            $html .= $path;
+            $html .= ' / ';
+        }
+        $html .= '</div>';
+
+        $html .= '<div id="'.$htmlid.'" class="filemanager-container">';
+        if (empty($tree->tree)) {
+            $html .= get_string('nofilesavailable', 'repository');
+        } else {
+            $html .= '<ul>';
+            foreach($tree->tree as $node) {
+                $link_attributes = array();
+                if (!empty($node['isdir'])) {
+                    $class = ' class="file-tree-folder"';
+                    $restore_link = '';
+                } else {
+                    $class = ' class="file-tree-file"';
+                    $link_attributes['target'] = '_blank';
+                    $restore_link = html_writer::link($node['restoreurl'], get_string('restore', 'moodle'), $link_attributes);
+                }
+                $html .= '<li '.$class.'>';
+                $html .= html_writer::link($node['url'], $node['filename'], $link_attributes);
+                // when js is off, use this restore link
+                // otherwise, yui treeview will generate a restore link in js
+                $html .= ' '.$restore_link;
+                $html .= '</li>';
+            }
+            $html .= '</ul>';
+        }
+        $html .= '</div>';
+        return $html;
+    }
+}
+/**
+ * Data structure representing backup files viewer
+ *
+ * @copyright 2010 Dongsheng Cai
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since     Moodle 2.0
+ */
+class backup_files_viewer implements renderable {
+    public $tree;
+    public $path;
+
+    /**
+     * Constructor of backup_files_viewer class
+     * @param file_info $file_info
+     * @param array $options
+     */
+    public function __construct(file_info $file_info, array $options = null) {
+        global $CFG;
+        $this->options = (array)$options;
+
+        $this->tree = array();
+        $children = $file_info->get_children();
+        $parent_info = $file_info->get_parent();
+
+        $level = $parent_info;
+        $this->path = array();
+        while ($level) {
+            $params = $level->get_params();
+            $context = get_context_instance_by_id($params['contextid']);
+            // lock user in course level
+            if ($context->contextlevel == CONTEXT_COURSECAT or $context->contextlevel == CONTEXT_SYSTEM) {
+                break;
+            }
+            $url = new moodle_url('/backup/restorefile.php', $params);
+            $this->path[] = html_writer::link($url->out(false), $level->get_visible_name());
+            $level = $level->get_parent();
+        }
+        $this->path = array_reverse($this->path);
+        $this->path[] = $file_info->get_visible_name();
+
+        foreach ($children as $child) {
+            $filedate = $child->get_timemodified();
+            $filesize = $child->get_filesize();
+            $mimetype = $child->get_mimetype();
+            $params = $child->get_params();
+            $fileitem = array(
+                    'params'   => $params,
+                    'filename' => $child->get_visible_name(),
+                    'filedate' => $filedate ? userdate($filedate) : '',
+                    'filesize' => $filesize ? display_size($filesize) : ''
+                    );
+            if ($child->is_directory()) {
+                // ignore all other fileares except backup_course backup_section and backup_activity
+                if ($params['component'] != 'backup' or !in_array($params['filearea'], array('course', 'section', 'activity'))) {
+                    continue;
+                }
+                $fileitem['isdir'] = true;
+                // link to this folder
+                $folderurl = new moodle_url('/backup/restorefile.php', $params);
+                $fileitem['url'] = $folderurl->out(false);
+            } else {
+                $restoreurl = new moodle_url('/backup/restorefile.php', array_merge($params, array('action'=>'choosebackupfile')));
+                // link to this file
+                $fileitem['url'] = $child->get_url();
+                $fileitem['restoreurl'] = $restoreurl->out(false);
+            }
+            $this->tree[] = $fileitem;
+        }
+    }
+}
Index: lang/en/backup.php
=========================================================
--- lang/en/backup.php	(revision 1.7)
+++ lang/en/backup.php	Thu Jul 08 11:35:34 WST 2010
@@ -73,5 +73,8 @@
 $string['onstage8action'] = 'Continue';
 $string['onstage16action'] = 'Continue';
 $string['previousstage'] = 'Previous';
+$string['restoreactivity'] = 'Restore activity';
+$string['restorecourse'] = 'Restore course';
+$string['restoresection'] = 'Restore section';
 $string['rootsettings'] = 'Backup settings';
-$string['scheduledsettings'] = 'Scheduled backup settings';
\ No newline at end of file
+$string['scheduledsettings'] = 'Scheduled backup settings';
