MDL-14589 initial file storage implementation, temporary file manager, migration...
authorskodak <skodak>
Thu, 31 Jul 2008 22:15:30 +0000 (22:15 +0000)
committerskodak <skodak>
Thu, 31 Jul 2008 22:15:30 +0000 (22:15 +0000)
48 files changed:
admin/roles/manage.php
admin/uploadpicture.php
blog/edit.php
blog/edit_form.php
blog/lib.php
config-dist.php
draftfile.php [new file with mode: 0644]
file.php
files/index.php
group/group.php
group/group_form.php
group/lib.php
lang/en_utf8/error.php
lib/db/access.php
lib/db/install.xml
lib/db/upgrade.php
lib/db/upgradelib.php
lib/editor/htmlarea/coursefiles.php
lib/editor/tinymce/coursefiles.php
lib/file/file_browser.php [new file with mode: 0644]
lib/file/file_exceptions.php [new file with mode: 0644]
lib/file/file_info.php [new file with mode: 0644]
lib/file/file_info_course.php [new file with mode: 0644]
lib/file/file_info_coursecat.php [new file with mode: 0644]
lib/file/file_info_coursefile.php [new file with mode: 0644]
lib/file/file_info_stored.php [new file with mode: 0644]
lib/file/file_info_system.php [new file with mode: 0644]
lib/file/file_info_user.php [new file with mode: 0644]
lib/file/file_packer.php [new file with mode: 0644]
lib/file/file_storage.php [new file with mode: 0644]
lib/file/stored_file.php [new file with mode: 0644]
lib/filelib.php
lib/formslib.php
lib/gdlib.php
lib/moodlelib.php
lib/weblib.php
mod/assignment/db/upgrade.php
mod/assignment/lib.php
mod/assignment/type/upload/assignment.class.php
mod/assignment/type/uploadsingle/assignment.class.php
mod/assignment/upload.php
mod/assignment/version.php
pluginfile.php [new file with mode: 0644]
user/edit_form.php
user/editadvanced_form.php
user/editlib.php
userfile.php [new file with mode: 0644]
version.php

index 254db54..5429996 100755 (executable)
@@ -55,7 +55,7 @@
         case 'add':
             if ($data = data_submitted() and confirm_sesskey()) {
 
-                $shortname = moodle_strtolower(clean_param(clean_filename($shortname), PARAM_SAFEDIR)); // only lowercase safe ASCII characters
+                $shortname = moodle_strtolower(clean_filename($shortname)); // only lowercase safe ASCII characters
                 $legacytype = required_param('legacytype', PARAM_RAW);
 
                 $legacyroles = get_legacy_roles();
index bce28a8..65566ce 100644 (file)
@@ -79,14 +79,14 @@ if ($formdata = $mform->get_data()) {
         
         // Create a unique temporary directory, to process the zip file
         // contents.
-        $zipdir = my_mktempdir($CFG->dataroot.'/temp/', 'usrpic');
+        $zipodir = my_mktempdir($CFG->dataroot.'/temp/', 'usrpic');
+        $dstfile = $zipodir.'/images.zip';
         
-        if (!$mform->save_files($zipdir)) {
+        if (!$mform->save_file('userfile', $dstfile, true)) {
             notify(get_string('uploadpicture_cannotmovezip','admin'));
             @remove_dir($zipdir);
         } else {
-            $dstfile = $zipdir.'/'.$mform->get_new_filename();
-            if(!unzip_file($dstfile, $zipdir, false)) {
+            if (!unzip_file($dstfile, $zipdir, false)) {
                 notify(get_string('uploadpicture_cannotunzip','admin'));
                 @remove_dir($zipdir);
             } else {
index 42b8944..fb46a81 100755 (executable)
@@ -155,11 +155,11 @@ die;
 function do_delete($post) {
     global $returnurl, $DB;
 
+    blog_delete_attachments($post);
+
     $status = $DB->delete_records('post', array('id'=>$post->id));
     tag_set('post', $post->id, array());
     
-    blog_delete_old_attachments($post);
-
     add_to_log(SITEID, 'blog', 'delete', 'index.php?userid='. $post->userid, 'deleted blog entry with entry id# '. $post->id);
 
     if (!$status) {
@@ -182,10 +182,12 @@ function do_add($post, $blogeditform) {
     if ($id = $DB->insert_record('post', $post)) {
         $post->id = $id;
         // add blog attachment
-        $dir = blog_file_area_name($post);
-        if ($blogeditform->save_files($dir) and $newfilename = $blogeditform->get_new_filename()) {
-            $DB->set_field("post", "attachment", $newfilename, array("id"=>$post->id));
+        if ($blogeditform->get_new_filename('attachment')) {
+            if ($blogeditform->save_stored_file('attachment', SYSCONTEXTID, 'blog', $post->id, '/', false, $USER->id)) {
+                $DB->set_field("post", "attachment", 1, array("id"=>$post->id));
+            }
         }
+
         add_tags_info($post->id);
         add_to_log(SITEID, 'blog', 'add', 'index.php?userid='.$post->userid.'&postid='.$post->id, $post->subject);
 
@@ -205,9 +207,13 @@ function do_edit($post, $blogeditform) {
 
     $post->lastmodified = time();
 
-    $dir = blog_file_area_name($post);
-    if ($blogeditform->save_files($dir) and $newfilename = $blogeditform->get_new_filename()) {
-        $post->attachment = $newfilename;
+    if ($blogeditform->get_new_filename('attachment')) {
+        blog_delete_attachments($post);
+        if ($blogeditform->save_stored_file('attachment', SYSCONTEXTID, 'blog', $post->id, '/', false, $USER->id)) {
+            $post->attachment = 1;
+        } else {
+            $post->attachment = 1;
+        }
     }
 
     // update record
index 4d47139..14fdd40 100644 (file)
@@ -12,9 +12,6 @@ class blog_edit_form extends moodleform {
         $post = $this->_customdata['existing'];
         $sitecontext = $this->_customdata['sitecontext'];
 
-        // the upload manager is used directly in entry processing, moodleform::save_files() is not used yet
-        $this->set_upload_manager(new upload_manager('attachment', true, false, $COURSE, false, 0, true, true, false));
-
         $mform->addElement('header', 'general', get_string('general', 'form'));
         $mform->addElement('text', 'subject', get_string('entrytitle', 'blog'), 'size="60"');
         $mform->setType('subject', PARAM_TEXT);
index f37e084..de5501c 100755 (executable)
         if($blogEntry->created != $blogEntry->lastmodified){
             $template['lastmod'] = userdate($blogEntry->lastmodified);
         }
-        
+
         $template['publishstate'] = $blogEntry->publishstate;
 
         /// preventing user to browse blogs that they aren't supposed to see
                 print(get_string('tags', 'tag') .': '. $blogtags);
            }
             echo '</div>';
-        } 
+        }
 
     /// Commands
 
 
     }
 
-    /**
-     * Creates a directory file name, suitable for make_upload_directory()
-     * $CFG->dataroot/blog/attachments/xxxx/file.jpg
-     */
-    function blog_file_area_name($blogentry) {
-        return "blog/attachments/$blogentry->id";
-    }
-
-    function blog_file_area($blogentry) {
-        return make_upload_directory( blog_file_area_name($blogentry) );
-    }
-
     /**
      * Deletes all the user files in the attachments area for a post
-     * EXCEPT for any file named $exception
      */
-    function blog_delete_old_attachments($post, $exception="") {
-        if ($basedir = blog_file_area($post)) {
-            if ($files = get_directory_list($basedir)) {
-                foreach ($files as $file) {
-                    if ($file != $exception) {
-                        unlink("$basedir/$file");
-                        notify("Existing file '$file' has been deleted!");
-                    }
-                }
-            }
-            if (!$exception) {  // Delete directory as well, if empty
-                rmdir("$basedir");
-            }
-        }
+    function blog_delete_attachments($post) {
+        $fs = get_file_storage();
+        $fs->delete_area_files(SYSCONTEXTID, 'blog', $post->id);
     }
 
     /**
     function blog_print_attachments($blogentry, $return=NULL) {
         global $CFG;
 
-        $filearea = blog_file_area_name($blogentry);
+        require_once($CFG->libdir.'/filelib.php');
+
+        $fs = get_file_storage();
+        $browser = get_file_browser();
+
+        $files = $fs->get_area_files(SYSCONTEXTID, 'blog', $blogentry->id);
 
         $imagereturn = "";
         $output = "";
 
-        if ($basedir = blog_file_area($blogentry)) {
-            if ($files = get_directory_list($basedir)) {
-                $strattachment = get_string("attachment", "forum");
-                foreach ($files as $file) {
-                    include_once($CFG->libdir.'/filelib.php');
-                    $icon = mimeinfo("icon", $file);
-                    $type = mimeinfo("type", $file);
-                    $ffurl = get_file_url("$filearea/$file");
-                    $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
+        $strattachment = get_string("attachment", "forum");
 
-                    if ($return == "html") {
-                        $output .= "<a href=\"$ffurl\">$image</a> ";
-                        $output .= "<a href=\"$ffurl\">$file</a><br />";
+        foreach ($files as $file) {
+            if ($file->is_directory()) {
+                continue;
+            }
 
-                    } else if ($return == "text") {
-                        $output .= "$strattachment $file:\n$ffurl\n";
+            $filename = $file->get_filename();
+            $ffurl    = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog/'.$blogentry->id.'/'.$filename);
+            $type     = $file->get_mimetype();
+            $icon     = mimeinfo_from_type("icon", $type);
+            $type     = mimeinfo_from_type("type", $type);
 
-                    } else {
-                        if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
-                            $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
-                        } else {
-                            echo "<a href=\"$ffurl\">$image</a> ";
-                            echo filter_text("<a href=\"$ffurl\">$file</a><br />");
-                        }
-                    }
+            $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
+
+            if ($return == "html") {
+                $output .= "<a href=\"$ffurl\">$image</a> ";
+                $output .= "<a href=\"$ffurl\">$filename</a><br />";
+
+            } else if ($return == "text") {
+                $output .= "$strattachment $filename:\n$ffurl\n";
+
+            } else {
+                if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
+                    $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
+                } else {
+                    echo "<a href=\"$ffurl\">$image</a> ";
+                    echo filter_text("<a href=\"$ffurl\">$filename</a><br />");
                 }
             }
         }
     function blog_get_participants() {
         global $CFG, $DB;
 
-        return $DB->get_records_sql("SELECT userid AS id 
+        return $DB->get_records_sql("SELECT userid AS id
                                        FROM {post}
                                       WHERE module = 'blog' AND courseid = 0");
     }
index 98015a1..c0c29d9 100644 (file)
@@ -164,13 +164,6 @@ $CFG->admin = 'admin';
 //      $CFG->defaultblocks = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity';
 //
 //
-// Allow unicode characters in uploaded files, generated reports, etc.
-// This setting is new and not much tested, there are known problems
-// with backup/restore that will not be solved, because native infozip
-// binaries are doing some weird conversions - use internal PHP zipping instead.
-// NOT RECOMMENDED FOR PRODUCTION SITES
-//     $CFG->unicodecleanfilename = true;
-//
 // Seconds for files to remain in caches. Decrease this if you are worried
 // about students being served outdated versions of uploaded files.
 //     $CFG->filelifetime = 86400;
diff --git a/draftfile.php b/draftfile.php
new file mode 100644 (file)
index 0000000..41d10f4
--- /dev/null
@@ -0,0 +1 @@
+<?php  //$Id$
index 23e4849..6cee7c1 100644 (file)
--- a/file.php
+++ b/file.php
@@ -13,7 +13,7 @@
 
       //TODO: Blog attachments do not have access control implemented - anybody can read them!
       //      It might be better to move the code to separate file because the access
-      //      control is quite complex - see bolg/index.php 
+      //      control is quite complex - see bolg/index.php
 
     require_once('config.php');
     require_once('lib/filelib.php');
@@ -29,7 +29,7 @@
 
     $relativepath = get_file_argument('file.php');
     $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
-    
+
     // relative path must start with '/', because of backup/restore!!!
     if (!$relativepath) {
         print_error('invalidargorconf');
         print_error('pathdoesnotstartslash');
     }
 
-    $pathname = $CFG->dataroot.$relativepath;
-
     // extract relative path components
-    $args = explode('/', trim($relativepath, '/'));
+    $args = explode('/', ltrim($relativepath, '/'));
+
     if (count($args) == 0) { // always at least courseid, may search for index.html in course root
         print_error('invalidarguments');
     }
-  
-    // security: limit access to existing course subdirectories
-    if (($args[0]!='blog') and (!$course = $DB->get_record_sql("SELECT * FROM {course} WHERE id=?", array((int)$args[0])))) {
-        print_error('invalidcourseid');
-    }
 
-    // security: prevent access to "000" or "1 something" directories
-    // hack for blogs, needs proper security check too
-    if (($args[0] != 'blog') and ($args[0] != $course->id)) {
+    $courseid = (int)array_shift($args);
+    $relativepath = '/'.implode('/', $args);
+
+    // security: limit access to existing course subdirectories
+    if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
         print_error('invalidcourseid');
     }
 
-    // security: login to course if necessary
-    // Note: file.php always calls require_login() with $setwantsurltome=false
-    //       in order to avoid messing redirects. MDL-14495
-    if ($args[0] == 'blog') {
-        if (empty($CFG->bloglevel)) {
-            print_error('blogdisable', 'blog');
-        } else if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
-            require_login(0, true, null, false);
-        } else if ($CFG->forcelogin) {
-            require_login(0, true, null, false);
-        }
-    } else if ($course->id != SITEID) {
+    if ($course->id != SITEID) {
         require_login($course->id, true, null, false);
+
     } else if ($CFG->forcelogin) {
         if (!empty($CFG->sitepolicy)
             and ($CFG->sitepolicy == $CFG->wwwroot.'/file.php'.$relativepath
         }
     }
 
-    // security: only editing teachers can access backups
-    if ((count($args) >= 2) and (strtolower($args[1]) == 'backupdata')) {
-        if (!has_capability('moodle/site:backup', get_context_instance(CONTEXT_COURSE, $course->id))) {
-            print_error('nopermissions');
-        } else {
-            $lifetime = 0; //disable browser caching for backups 
-        }
-    }
+    $context = get_context_instance(CONTEXT_COURSE, $course->id);
 
-    if (is_dir($pathname)) {
-        if (file_exists($pathname.'/index.html')) {
-            $pathname = rtrim($pathname, '/').'/index.html';
-            $args[] = 'index.html';
-        } else if (file_exists($pathname.'/index.htm')) {
-            $pathname = rtrim($pathname, '/').'/index.htm';
-            $args[] = 'index.htm';
-        } else if (file_exists($pathname.'/Default.htm')) {
-            $pathname = rtrim($pathname, '/').'/Default.htm';
-            $args[] = 'Default.htm';
-        } else {
-            // security: do not return directory node!
-            not_found($course->id);
-        }
-    }
-
-    // security: teachers can view all assignments, students only their own
-    if ((count($args) >= 3)
-        and (strtolower($args[1]) == 'moddata')
-        and (strtolower($args[2]) == 'assignment')) {
+    $fs = get_file_storage();
 
-        $lifetime = 0;  // do not cache assignments, students may reupload them
-        if ($args[4] == $USER->id) {
-            //can view own assignemnt submissions
-        } else {
-            $instance = (int)$args[3];
-            if (!$cm = get_coursemodule_from_instance('assignment', $instance, $course->id)) {
-                not_found($course->id);
-            }
-            if (!has_capability('mod/assignment:grade', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-                print_error('nopermissions');
-            }
-        } 
-    }
+    $fullpath = $context->id.'course_content0'.$relativepath;
 
-    // security: force download of all attachments submitted by students
-    if ((count($args) >= 3)
-        and (strtolower($args[1]) == 'moddata')
-        and ((strtolower($args[2]) == 'forum')
-            or (strtolower($args[2]) == 'assignment')
-            or (strtolower($args[2]) == 'data')
-            or (strtolower($args[2]) == 'glossary')
-            or (strtolower($args[2]) == 'wiki')
-            or (strtolower($args[2]) == 'exercise')
-            or (strtolower($args[2]) == 'workshop')
-            )) {
-        $forcedownload  = 1; // force download of all attachments
-    }
-    if ($args[0] == 'blog') {
-        $forcedownload  = 1; // force download of all attachments
-    }    
-
-    // security: some protection of hidden resource files
-    // warning: it may break backwards compatibility
-    if ((!empty($CFG->preventaccesstohiddenfiles)) 
-        and (count($args) >= 2)
-        and (!(strtolower($args[1]) == 'moddata' and strtolower($args[2]) != 'resource')) // do not block files from other modules!
-        and (!has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_COURSE, $course->id)))) {
-
-        $rargs = $args;
-        array_shift($rargs);
-        $reference = implode('/', $rargs);
-
-        $sql = "SELECT COUNT(r.id)
-                  FROM {resource} r, {course_modules} cm, {modules} m
-                 WHERE r.course        = ?
-                       AND m.name      = 'resource'
-                       AND cm.module   = m.id
-                       AND cm.instance = r.id
-                       AND cm.visible  = 0
-                       AND r.type      = 'file'
-                       AND r.reference = ?";
-        $params = array($course->id, $reference);
-
-        if ($DB->count_records_sql($sql, $params)) {
-           print_error('nopermissions');
+    if (!$file = $fs->get_file_by_hash(sha1($fullpath))) {
+        if (strrpos($fullpath, '/') !== strlen($fullpath) -1 ) {
+            $fullpath .= '/';
+        }
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath.'/.'))) {
+            not_found();
         }
     }
-
-    // check that file exists
-    if (!file_exists($pathname)) {
-        not_found($course->id);
+    // do not serve dirs
+    if ($file->get_filename() == '.') {
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath.'index.html'))) {
+            if (!$file = $fs->get_file_by_hash(sha1($fullpath.'index.htm'))) {
+                if (!$file = $fs->get_file_by_hash(sha1($fullpath.'Default.htm'))) {
+                    not_found();
+                }
+            }
+        }
     }
 
     // ========================================
     // finally send the file
     // ========================================
     session_write_close(); // unlock session during fileserving
-    $filename = $args[count($args)-1];
-    send_file($pathname, $filename, $lifetime, $CFG->filteruploadedfiles, false, $forcedownload);
+    send_stored_file($file, $lifetime, $CFG->filteruploadedfiles, $forcedownload);
 
-    function not_found($courseid) {
-        global $CFG;
+    function not_found() {
+        global $CFG, $COURSE;
         header('HTTP/1.0 404 not found');
-        print_error('filenotfound', 'error', $CFG->wwwroot.'/course/view.php?id='.$courseid); //this is not displayed on IIS??
+        print_error('filenotfound', 'error', $CFG->wwwroot.'/course/view.php?id='.$COURSE->id); //this is not displayed on IIS??
     }
-?>
+
index e869873..b76d57a 100644 (file)
 <?php // $Id$
 
-//  Manage all uploaded files in a course file area
-
-//  All the Moodle-specific stuff is in this top section
-//  Configuration and access control occurs here.
-//  Must define:  USER, basedir, baseweb, html_header and html_footer
-//  USER is a persistent variable using sessions
-
     require('../config.php');
-    require($CFG->libdir.'/filelib.php');
-    require($CFG->libdir.'/adminlib.php');
-
-    $id      = required_param('id', PARAM_INT);
-    $file    = optional_param('file', '', PARAM_PATH);
-    $wdir    = optional_param('wdir', '', PARAM_PATH);
-    $action  = optional_param('action', '', PARAM_ACTION);
-    $name    = optional_param('name', '', PARAM_FILE);
-    $oldname = optional_param('oldname', '', PARAM_FILE);
-    $choose  = optional_param('choose', '', PARAM_FILE); //in fact it is always 'formname.inputname'
-    $userfile= optional_param('userfile','',PARAM_FILE);
-    $save    = optional_param('save', 0, PARAM_BOOL);
-    $text    = optional_param('text', '', PARAM_RAW);
-    $confirm = optional_param('confirm', 0, PARAM_BOOL);
-
-    if ($choose) {
-        if (count(explode('.', $choose)) > 2) {
-            print_error('invalidformatpara');
-        }
-    }
-
+    require_once($CFG->libdir.'/filelib.php');
+    require_once($CFG->libdir.'/adminlib.php');
 
-    if (! $course = $DB->get_record("course", array("id"=>$id))) {
-        print_error('invalidcourseid');
-    }
+    $courseid   = optional_param('id', 0, PARAM_INT);
 
-    require_login($course);
-
-    require_capability('moodle/course:managefiles', get_context_instance(CONTEXT_COURSE, $course->id));
-
-    function html_footer() {
-        global $COURSE, $choose;
-
-        echo '</td></tr></table>';
-
-        print_footer($COURSE);
-    }
+    $contextid  = optional_param('contextid', SYSCONTEXTID, PARAM_INT);
+    $filearea   = optional_param('filearea', '', PARAM_SAFEDIR);
+    $itemid     = optional_param('itemid', -1, PARAM_INT);
+    $filepath   = optional_param('filepath', '', PARAM_PATH);
+    $filename   = optional_param('filename', '', PARAM_FILE);
 
-    function html_header($course, $wdir, $formfield=""){
-        global $CFG, $ME, $choose;
+    $newdirname = optional_param('newdirname', '', PARAM_FILE);
+    $delete     = optional_param('delete', 0, PARAM_BOOL);
 
-        $navlinks = array();
-        // $navlinks[] = array('name' => $course->shortname, 'link' => "../course/view.php?id=$course->id", 'type' => 'misc');
-
-        if ($course->id == SITEID) {
-            $strfiles = get_string("sitefiles");
-        } else {
-            $strfiles = get_string("files");
+    if ($courseid) {
+        if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
+            print_error('invalidcourseid');
         }
-
-        if ($wdir == "/") {
-            $navlinks[] = array('name' => $strfiles, 'link' => null, 'type' => 'misc');
-        } else {
-            $dirs = explode("/", $wdir);
-            $numdirs = count($dirs);
-            $link = "";
-            $navlinks[] = array('name' => $strfiles,
-                                'link' => $ME."?id=$course->id&amp;wdir=/&amp;choose=$choose",
-                                'type' => 'misc');
-
-            for ($i=1; $i<$numdirs-1; $i++) {
-                $link .= "/".urlencode($dirs[$i]);
-                $navlinks[] = array('name' => $dirs[$i],
-                                    'link' => $ME."?id=$course->id&amp;wdir=$link&amp;choose=$choose",
-                                    'type' => 'misc');
-            }
-            $navlinks[] = array('name' => $dirs[$numdirs-1], 'link' => null, 'type' => 'misc');
+        if (!$context = get_context_instance(CONTEXT_COURSE, $course->id)) {
+            print_error('invalidcontext');
         }
-
-        $navigation = build_navigation($navlinks);
-
-        if ($choose) {
-            print_header();
-
-            $chooseparts = explode('.', $choose);
-            if (count($chooseparts)==2){
-            ?>
-            <script type="text/javascript">
-            //<![CDATA[
-            function set_value(txt) {
-                opener.document.forms['<?php echo $chooseparts[0]."'].".$chooseparts[1] ?>.value = txt;
-                window.close();
-            }
-            //]]>
-            </script>
-
-            <?php
-            } elseif (count($chooseparts)==1){
-            ?>
-            <script type="text/javascript">
-            //<![CDATA[
-            function set_value(txt) {
-                opener.document.getElementById('<?php echo $chooseparts[0] ?>').value = txt;
-                window.close();
-            }
-            //]]>
-            </script>
-
-            <?php
-
-            }
-            $fullnav = '';
-            $i = 0;
-            foreach ($navlinks as $navlink) {
-                // If this is the last link do not link
-                if ($i == count($navlinks) - 1) {
-                    $fullnav .= $navlink['name'];
-                } else {
-                    $fullnav .= '<a href="'.$navlink['link'].'">'.$navlink['name'].'</a>';
-                }
-                $fullnav .= ' -> ';
-                $i++;
-            }
-            $fullnav = substr($fullnav, 0, -4);
-            $fullnav = str_replace('->', '&raquo;', format_string($course->shortname) . " -> " . $fullnav);
-            echo '<div id="nav-bar">'.$fullnav.'</div>';
-
-            if ($course->id == SITEID and $wdir != "/backupdata") {
-                print_heading(get_string("publicsitefileswarning"), "center", 2);
-            }
-
-        } else {
-
-            if ($course->id == SITEID) {
-
-                if ($wdir == "/backupdata") {
-                    admin_externalpage_setup('frontpagerestore');
-                    admin_externalpage_print_header();
-                } else {
-                    admin_externalpage_setup('sitefiles');
-                    admin_externalpage_print_header();
-
-                    print_heading(get_string("publicsitefileswarning"), "center", 2);
-
-                }
-
-            } else {
-                print_header("$course->shortname: $strfiles", $course->fullname, $navigation,  $formfield);
-            }
-        }
-
-
-        echo "<table border=\"0\" style=\"margin-left:auto;margin-right:auto\" cellspacing=\"3\" cellpadding=\"3\" width=\"640\">";
-        echo "<tr>";
-        echo "<td colspan=\"2\">";
-
+        redirect('index.php?contextid='.$context->id.'&amp;filearea=coursefiles');
     }
 
-
-    if (! $basedir = make_upload_directory("$course->id")) {
-        print_error("nopermissiontomkdir");
+    if (!$context = get_context_instance_by_id($contextid)) {
+        print_error('invalidcontext');
     }
 
-    $baseweb = $CFG->wwwroot;
+    require_login();
+    require_capability('moodle/course:managefiles', $context);
 
-//  End of configuration and access control
-
-
-    if ($wdir == '') {
-        $wdir = "/";
+    if ($filearea === '') {
+        $filearea = null;
     }
 
-    if ($wdir{0} != '/') {  //make sure $wdir starts with slash
-        $wdir = "/".$wdir;
+    if ($itemid < 0) {
+        $itemid = null;
     }
 
-    if ($wdir == "/backupdata") {
-        if (! make_upload_directory("$course->id/backupdata")) {   // Backup folder
-            print_error('cannotcreatebackupdir');
-        }
+    if ($filepath === '') {
+        $filepath = null;
     }
 
-    if (!is_dir($basedir.$wdir)) {
-        html_header($course, $wdir);
-        print_error('nofolder', '', "$CFG->wwwroot/files/index.php?id=$id");
+    if ($filename === '') {
+        $filename = null;
     }
 
-    switch ($action) {
-
-        case "upload":
-            html_header($course, $wdir);
-            require_once($CFG->dirroot.'/lib/uploadlib.php');
-
-            if ($save and confirm_sesskey()) {
-                $course->maxbytes = 0;  // We are ignoring course limits
-                $um = new upload_manager('userfile',false,false,$course,false,0);
-                $dir = "$basedir$wdir";
-                if ($um->process_file_uploads($dir)) {
-                    notify(get_string('uploadedfile'));
-                }
-                // um will take care of error reporting.
-                displaydir($wdir);
-            } else {
-                $upload_max_filesize = get_max_upload_file_size($CFG->maxbytes);
-                $filesize = display_size($upload_max_filesize);
-
-                $struploadafile = get_string("uploadafile");
-                $struploadthisfile = get_string("uploadthisfile");
-                $strmaxsize = get_string("maxsize", "", $filesize);
-                $strcancel = get_string("cancel");
-
-                echo "<p>$struploadafile ($strmaxsize) --> <b>$wdir</b></p>";
-                echo "<form enctype=\"multipart/form-data\" method=\"post\" action=\"index.php\">";
-                echo "<div>";
-                echo "<table><tr><td colspan=\"2\">";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"upload\" />";
-                echo " <input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-                upload_print_form_fragment(1,array('userfile'),null,false,null,$upload_max_filesize,0,false);
-                echo " </td></tr></table>";
-                echo " <input type=\"submit\" name=\"save\" value=\"$struploadthisfile\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "<form action=\"index.php\" method=\"get\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                echo " <input type=\"submit\" value=\"$strcancel\" />";
-                echo "</div>";
-                echo "</form>";
-            }
-            html_footer();
-            break;
-
-        case "delete":
-            if ($confirm and confirm_sesskey()) {
-                html_header($course, $wdir);
-                if (!empty($USER->filelist)) {
-                    foreach ($USER->filelist as $file) {
-                        $fullfile = $basedir.'/'.$file;
-                        if (! fulldelete($fullfile)) {
-                            echo "<br />Error: Could not delete: $fullfile";
-                        }
-                    }
-                }
-                clearfilelist();
-                displaydir($wdir);
-                html_footer();
-
-            } else {
-                html_header($course, $wdir);
-
-                if (setfilelist($_POST)) {
-                    notify(get_string('deletecheckwarning').':');
-                    print_simple_box_start("center");
-                    printfilelist($USER->filelist);
-                    print_simple_box_end();
-                    echo "<br />";
-
-                    require_once($CFG->dirroot.'/mod/resource/lib.php');
-                    $block = resource_delete_warning($course, $USER->filelist);
-
-                    if (empty($CFG->resource_blockdeletingfile) or $block == '') {
-                        $optionsyes = array('id'=>$id, 'wdir'=>$wdir, 'action'=>'delete', 'confirm'=>1, 'sesskey'=>sesskey(), 'choose'=>$choose);
-                        $optionsno  = array('id'=>$id, 'wdir'=>$wdir, 'action'=>'cancel', 'choose'=>$choose);
-                        notice_yesno (get_string('deletecheckfiles'), 'index.php', 'index.php', $optionsyes, $optionsno, 'post', 'get');
-                    } else {
-
-                        notify(get_string('warningblockingdelete', 'resource'));
-                        $options  = array('id'=>$id, 'wdir'=>$wdir, 'action'=>'cancel', 'choose'=>$choose);
-                        print_continue("index.php?id=$id&amp;wdir=$wdir&amp;action=cancel&amp;choose=$choose");
-                    }
-                } else {
-                    displaydir($wdir);
-                }
-                html_footer();
-            }
-            break;
-
-        case "move":
-            html_header($course, $wdir);
-            if (($count = setfilelist($_POST)) and confirm_sesskey()) {
-                $USER->fileop     = $action;
-                $USER->filesource = $wdir;
-                echo "<p class=\"centerpara\">";
-                print_string("selectednowmove", "moodle", $count);
-                echo "</p>";
-            }
-            displaydir($wdir);
-            html_footer();
-            break;
-
-        case "paste":
-            html_header($course, $wdir);
-            if (isset($USER->fileop) and ($USER->fileop == "move") and confirm_sesskey()) {
-                foreach ($USER->filelist as $file) {
-                    $shortfile = basename($file);
-                    $oldfile = $basedir.'/'.$file;
-                    $newfile = $basedir.$wdir."/".$shortfile;
-                    if (!rename($oldfile, $newfile)) {
-                        echo "<p>Error: $shortfile not moved</p>";
-                    }
-                }
-            }
-            clearfilelist();
-            displaydir($wdir);
-            html_footer();
-            break;
-
-        case "rename":
-            if (($name != '') and confirm_sesskey()) {
-                html_header($course, $wdir);
-                $name = clean_filename($name);
-                if (file_exists($basedir.$wdir."/".$name)) {
-                    echo "<center>Error: $name already exists!</center>";
-                } else if (!rename($basedir.$wdir."/".$oldname, $basedir.$wdir."/".$name)) {
-                    echo "<p align=\"center\">Error: could not rename $oldname to $name</p>";
-                } else {
-                    //file was renamed now update resources if needed
-                    require_once($CFG->dirroot.'/mod/resource/lib.php');
-                    resource_renamefiles($course, $wdir, $oldname, $name);
-                }
-                displaydir($wdir);
-
-            } else {
-                $strrename = get_string("rename");
-                $strcancel = get_string("cancel");
-                $strrenamefileto = get_string("renamefileto", "moodle", $file);
-                html_header($course, $wdir, "form.name");
-                echo "<p>$strrenamefileto:</p>";
-                echo "<table><tr><td>";
-                echo "<form action=\"index.php\" method=\"post\">";
-                echo "<fieldset class=\"invisiblefieldset\">";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"rename\" />";
-                echo " <input type=\"hidden\" name=\"oldname\" value=\"$file\" />";
-                echo " <input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-                echo " <input type=\"text\" name=\"name\" size=\"35\" value=\"$file\" />";
-                echo " <input type=\"submit\" value=\"$strrename\" />";
-                echo "</fieldset>";
-                echo "</form>";
-                echo "</td><td>";
-                echo "<form action=\"index.php\" method=\"get\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                echo " <input type=\"submit\" value=\"$strcancel\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "</td></tr></table>";
-            }
-            html_footer();
-            break;
-
-        case "makedir":
-            if (($name != '') and confirm_sesskey()) {
-                html_header($course, $wdir);
-                $name = clean_filename($name);
-                if (file_exists("$basedir$wdir/$name")) {
-                    echo "Error: $name already exists!";
-                } else if (! make_upload_directory("$course->id$wdir/$name")) {
-                    echo "Error: could not create $name";
-                }
-                displaydir($wdir);
-
-            } else {
-                $strcreate = get_string("create");
-                $strcancel = get_string("cancel");
-                $strcreatefolder = get_string("createfolder", "moodle", $wdir);
-                html_header($course, $wdir, "form.name");
-                echo "<p>$strcreatefolder:</p>";
-                echo "<table><tr><td>";
-                echo "<form action=\"index.php\" method=\"post\">";
-                echo "<fieldset class=\"invisiblefieldset\">";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"makedir\" />";
-                echo " <input type=\"text\" name=\"name\" size=\"35\" />";
-                echo " <input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-                echo " <input type=\"submit\" value=\"$strcreate\" />";
-                echo "</fieldset>";
-                echo "</form>";
-                echo "</td><td>";
-                echo "<form action=\"index.php\" method=\"get\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                echo " <input type=\"submit\" value=\"$strcancel\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "</td></tr></table>";
-            }
-            html_footer();
-            break;
-
-        case "edit":
-            html_header($course, $wdir);
-            if (($text != '') and confirm_sesskey()) {
-                $fileptr = fopen($basedir.'/'.$file,"w");
-                $text = preg_replace('/\x0D/', '', $text);  // http://moodle.org/mod/forum/discuss.php?d=38860
-                fputs($fileptr, $text);
-                fclose($fileptr);
-                displaydir($wdir);
-
-            } else {
-                $streditfile = get_string("edit", "", "<b>$file</b>");
-                $fileptr  = fopen($basedir.'/'.$file, "r");
-                $contents = fread($fileptr, filesize($basedir.'/'.$file));
-                fclose($fileptr);
-
-                if (mimeinfo("type", $file) == "text/html") {
-                    $usehtmleditor = can_use_html_editor();
-                } else {
-                    $usehtmleditor = false;
-                }
-                $usehtmleditor = false;    // Always keep it off for now
-
-                print_heading("$streditfile");
-
-                echo "<table><tr><td colspan=\"2\">";
-                echo "<form action=\"index.php\" method=\"post\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"file\" value=\"$file\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"edit\" />";
-                echo " <input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-                print_textarea($usehtmleditor, 25, 80, 680, 400, "text", $contents);
-                echo "</td></tr><tr><td>";
-                echo " <input type=\"submit\" value=\"".get_string("savechanges")."\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "</td><td>";
-                echo "<form action=\"index.php\" method=\"get\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                echo " <input type=\"submit\" value=\"".get_string("cancel")."\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "</td></tr></table>";
-
-                if ($usehtmleditor) {
-                    use_html_editor();
-                }
-
-
-            }
-            html_footer();
-            break;
+    $error = '';
 
-        case "zip":
-            if (($name != '') and confirm_sesskey()) {
-                html_header($course, $wdir);
-                $name = clean_filename($name);
+    $browser = get_file_browser();
 
-                $files = array();
-                foreach ($USER->filelist as $file) {
-                   $files[] = "$basedir/$file";
-                }
+    $file_info = $browser->get_file_info($context, $filearea, $itemid, $filepath, $filename);
 
-                if (!zip_files($files,"$basedir$wdir/$name")) {
-                    print_error("zipfileserror", "error");
-                }
+/// process actions
+    if ($file_info and $file_info->is_directory() and $file_info->is_writable() and $newdirname !== '' and data_submitted() and confirm_sesskey()) {
+        if ($newdir_info = $file_info->create_directory($newdirname, $USER->id)) {
+            $params = $newdir_info->get_params_rawencoded();
+            $params = implode('&amp;', $params);
+            redirect("index.php?$params");
+        } else {
+            $error = "Could not create new dir"; // TODO: localise
+        }
+    }
 
-                clearfilelist();
-                displaydir($wdir);
+    if ($file_info and $file_info->is_directory() and $file_info->is_writable() and isset($_FILES['newfile']) and data_submitted() and confirm_sesskey()) {
+        $file = $_FILES['newfile'];
+        $newfilename = clean_param($file['name'], PARAM_FILE);
+        if (is_uploaded_file($_FILES['newfile']['tmp_name'])) {
+            try {
+                if ($newfile = $file_info->create_file_from_pathname($newfilename, $_FILES['newfile']['tmp_name'], $USER->id)) {
+                    $params = $file_info->get_params_rawencoded();
+                    $params = implode('&amp;', $params);
+                    redirect("index.php?$params");
 
-            } else {
-                html_header($course, $wdir, "form.name");
-
-                if (setfilelist($_POST)) {
-                    echo "<p align=\"center\">".get_string("youareabouttocreatezip").":</p>";
-                    print_simple_box_start("center");
-                    printfilelist($USER->filelist);
-                    print_simple_box_end();
-                    echo "<br />";
-                    echo "<p align=\"center\">".get_string("whattocallzip")."</p>";
-                    echo "<table><tr><td>";
-                    echo "<form action=\"index.php\" method=\"post\">";
-                    echo "<fieldset class=\"invisiblefieldset\">";
-                    echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                    echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                    echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                    echo " <input type=\"hidden\" name=\"action\" value=\"zip\" />";
-                    echo " <input type=\"text\" name=\"name\" size=\"35\" value=\"new.zip\" />";
-                    echo " <input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-                    echo " <input type=\"submit\" value=\"".get_string("createziparchive")."\" />";
-                    echo "<fieldset>";
-                    echo "</form>";
-                    echo "</td><td>";
-                    echo "<form action=\"index.php\" method=\"get\">";
-                    echo "<div>";
-                    echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                    echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                    echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                    echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                    echo " <input type=\"submit\" value=\"".get_string("cancel")."\" />";
-                    echo "</div>";
-                    echo "</form>";
-                    echo "</td></tr></table>";
                 } else {
-                    displaydir($wdir);
-                    clearfilelist();
+                    $error = "Could not create upload file"; // TODO: localise
                 }
+            } catch (file_exception $e) {
+                $error = "Exception: Could not create upload file"; // TODO: localise
             }
-            html_footer();
-            break;
-
-        case "unzip":
-            html_header($course, $wdir);
-            if (($file != '') and confirm_sesskey()) {
-                $strok = get_string("ok");
-                $strunpacking = get_string("unpacking", "", $file);
+        }
+    }
 
-                echo "<p align=\"center\">$strunpacking:</p>";
+    if ($file_info and $delete) {
+        if (!data_submitted() or !confirm_sesskey()) {
+            print_header();
+            notify(get_string('deletecheckwarning').': '.$file_info->get_visible_name());
+            $parent_info = $file_info->get_parent();
 
-                $file = basename($file);
+            $optionsno  = $parent_info->get_params();
+            $optionsyes = $file_info->get_params();
+            $optionsyes['delete'] = 1;
+            $optionsyes['sesskey'] = sesskey();
 
-                if (!unzip_file("$basedir$wdir/$file")) {
-                    print_error("unzipfileserror", "error");
-                }
-
-                echo "<div style=\"text-align:center\"><form action=\"index.php\" method=\"get\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                echo " <input type=\"submit\" value=\"$strok\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "</div>";
-            } else {
-                displaydir($wdir);
-            }
-            html_footer();
-            break;
-
-        case "listzip":
-            html_header($course, $wdir);
-            if (($file != '') and confirm_sesskey()) {
-                $strname = get_string("name");
-                $strsize = get_string("size");
-                $strmodified = get_string("modified");
-                $strok = get_string("ok");
-                $strlistfiles = get_string("listfiles", "", $file);
-
-                echo "<p align=\"center\">$strlistfiles:</p>";
-                $file = basename($file);
-
-                include_once("$CFG->libdir/pclzip/pclzip.lib.php");
-                $archive = new PclZip(cleardoubleslashes("$basedir$wdir/$file"));
-                if (!$list = $archive->listContent(cleardoubleslashes("$basedir$wdir"))) {
-                    notify($archive->errorInfo(true));
+            notice_yesno (get_string('deletecheckfiles'), 'index.php', 'index.php', $optionsyes, $optionsno, 'post', 'get');
+            print_footer();
+            die;
+        }
 
-                } else {
-                    echo "<table cellpadding=\"4\" cellspacing=\"2\" border=\"0\" width=\"640\" class=\"files\">";
-                    echo "<tr class=\"file\"><th align=\"left\" class=\"header name\" scope=\"col\">$strname</th><th align=\"right\" class=\"header size\" scope=\"col\">$strsize</th><th align=\"right\" class=\"header date\" scope=\"col\">$strmodified</th></tr>";
-                    foreach ($list as $item) {
-                        echo "<tr>";
-                        print_cell("left", s($item['filename']), 'name');
-                        if (! $item['folder']) {
-                            print_cell("right", display_size($item['size']), 'size');
-                        } else {
-                            echo "<td>&nbsp;</td>";
-                        }
-                        $filedate  = userdate($item['mtime'], get_string("strftimedatetime"));
-                        print_cell("right", $filedate, 'date');
-                        echo "</tr>";
-                    }
-                    echo "</table>";
-                }
-                echo "<br /><center><form action=\"index.php\" method=\"get\">";
-                echo "<div>";
-                echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-                echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-                echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-                echo " <input type=\"hidden\" name=\"action\" value=\"cancel\" />";
-                echo " <input type=\"submit\" value=\"$strok\" />";
-                echo "</div>";
-                echo "</form>";
-                echo "</center>";
-            } else {
-                displaydir($wdir);
+        if ($parent_info = $file_info->get_parent() and $parent_info->is_writable()) {
+            if (!$file_info->delete()) {
+                $error = "Could not delete file!"; // TODO: localise
             }
-            html_footer();
-            break;
-
-        case "restore":
-            html_header($course, $wdir);
-            if (($file != '') and confirm_sesskey()) {
-                echo "<p align=\"center\">".get_string("youaregoingtorestorefrom").":</p>";
-                print_simple_box_start("center");
-                echo $file;
-                print_simple_box_end();
-                echo "<br />";
-                echo "<p align=\"center\">".get_string("areyousuretorestorethisinfo")."</p>";
-                $restore_path = "$CFG->wwwroot/backup/restore.php";
-                notice_yesno (get_string("areyousuretorestorethis"),
-                                $restore_path."?id=".$id."&amp;file=".cleardoubleslashes($id.$wdir."/".$file)."&amp;method=manual",
-                                "index.php?id=$id&amp;wdir=$wdir&amp;action=cancel");
-            } else {
-                displaydir($wdir);
-            }
-            html_footer();
-            break;
-
-        case "cancel":
-            clearfilelist();
-
-        default:
-            html_header($course, $wdir);
-            displaydir($wdir);
-            html_footer();
-            break;
-}
-
-
-/// FILE FUNCTIONS ///////////////////////////////////////////////////////////
+            $params = $parent_info->get_params_rawencoded();
+            $params = implode('&amp;', $params);
+            redirect("index.php?$params", $error);
+        }
+    }
 
 
-function setfilelist($VARS) {
-    global $USER;
+/// print dir listing
+    html_header($context, $file_info);
 
-    $USER->filelist = array ();
-    $USER->fileop = "";
+    if ($error !== '') {
+        notify($error);
+    }
 
-    $count = 0;
-    foreach ($VARS as $key => $val) {
-        if (substr($key,0,4) == "file") {
-            $count++;
-            $val = rawurldecode($val);
-            $USER->filelist[] = clean_param($val, PARAM_PATH);
-        }
+    displaydir($file_info);
+
+    if ($file_info and $file_info->is_directory() and $file_info->is_writable()) {
+        echo '<br />';
+
+        echo '<form action="index.php" method="post"><div>';
+        echo '<input type="hidden" name="contextid" value="'.$contextid.'" />';
+        echo '<input type="hidden" name="filearea" value="'.$filearea.'" />';
+        echo '<input type="hidden" name="itemid" value="'.$itemid.'" />';
+        echo '<input type="hidden" name="filepath" value="'.s($filepath).'" />';
+        echo '<input type="hidden" name="filename" value="'.s($filename).'" />';
+        echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+        echo '<input type="text" name="newdirname" value="" />';
+        echo '<input type="submit" value="'.get_string('makeafolder').'" />';
+        echo '</div></form>';
+
+        echo '<br />';
+
+        echo '<form enctype="multipart/form-data" method="post" action="index.php">';
+        echo '<input type="hidden" name="contextid" value="'.$contextid.'" />';
+        echo '<input type="hidden" name="filearea" value="'.$filearea.'" />';
+        echo '<input type="hidden" name="itemid" value="'.$itemid.'" />';
+        echo '<input type="hidden" name="filepath" value="'.s($filepath).'" />';
+        echo '<input type="hidden" name="filename" value="'.s($filename).'" />';
+        echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+        echo '<input name="newfile" type="file" />';
+        echo '<input type="submit" value="'.get_string('uploadafile').'" />';
+        echo '</div></form>';
     }
-    return $count;
-}
 
-function clearfilelist() {
-    global $USER;
+    html_footer();
 
-    $USER->filelist = array ();
-    $USER->fileop = "";
+/// UI functions /////////////////////////
+
+function html_footer() {
+    echo '</td></tr></table>';
+    print_footer();
 }
 
+function html_header($context, $file_info){
+    global $CFG, $SITE;
 
-function printfilelist($filelist) {
-    global $CFG, $basedir;
+    $navlinks = array();
+    $strfiles = get_string("files");
 
-    $strfolder = get_string("folder");
-    $strfile   = get_string("file");
+    $navlinks[] = array('name' => $strfiles, 'link' => null, 'type' => 'misc');
 
-    foreach ($filelist as $file) {
-        if (is_dir($basedir.'/'.$file)) {
-            echo '<img src="'. $CFG->pixpath .'/f/folder.gif" class="icon" alt="'. $strfolder .'" /> '. htmlspecialchars($file) .'<br />';
-            $subfilelist = array();
-            $currdir = opendir($basedir.'/'.$file);
-            while (false !== ($subfile = readdir($currdir))) {
-                if ($subfile <> ".." && $subfile <> ".") {
-                    $subfilelist[] = $file."/".$subfile;
-                }
-            }
-            printfilelist($subfilelist);
+    $navigation = build_navigation($navlinks);
+    print_header("$SITE->shortname: $strfiles", '', $navigation);
 
-        } else {
-            $icon = mimeinfo("icon", $file);
-            echo '<img src="'. $CFG->pixpath .'/f/'. $icon .'" class="icon" alt="'. $strfile .'" /> '. htmlspecialchars($file) .'<br />';
-        }
-    }
+    echo "<table border=\"0\" style=\"margin-left:auto;margin-right:auto\" cellspacing=\"3\" cellpadding=\"3\" width=\"740\">";
+    echo "<tr>";
+    echo "<td colspan=\"2\">";
 }
 
+/// FILE FUNCTIONS ///////////////////////////////////////////////////////////
 
 function print_cell($alignment='center', $text='&nbsp;', $class='') {
     if ($class) {
@@ -692,60 +182,42 @@ function print_cell($alignment='center', $text='&nbsp;', $class='') {
     echo '<td align="'.$alignment.'" style="white-space:nowrap "'.$class.'>'.$text.'</td>';
 }
 
-function displaydir ($wdir) {
-//  $wdir == / or /a or /a/b/c/d  etc
+function displaydir($file_info) {
+    global $CFG;
+
+    $children = $file_info->get_children();
+    $parent_info = $file_info->get_parent();
+
+    $strname     = get_string('name');
+    $strsize     = get_string('size');
+    $strmodified = get_string('modified');
+    $strfolder   = get_string('folder');
+    $strfile     = get_string('file');
+    $strdownload = get_string('download');
+    $strdelete   = get_string('delete');
+    $straction   = get_string('action');
+
+    $path = array();
+    $params = $file_info->get_params_rawencoded();
+    $params = implode('&amp;', $params);
+    $path[] = $file_info->get_visible_name();
+
+    $level = $parent_info;
+    while ($level) {
+        $params = $level->get_params_rawencoded();
+        $params = implode('&amp;', $params);
+        $path[] = '<a href="index.php?'.$params.'">'.$level->get_visible_name().'</a>';
+        $level = $level->get_parent();
+    }
 
-    global $basedir;
-    global $id;
-    global $USER, $CFG;
-    global $choose;
+    $path = array_reverse($path);
 
-    $fullpath = $basedir.$wdir;
-    $dirlist = array();
+    $path = implode (' / ', $path);
+    echo $path. ' /';
 
-    $directory = opendir($fullpath);             // Find all files
-    while (false !== ($file = readdir($directory))) {
-        if ($file == "." || $file == "..") {
-            continue;
-        }
-
-        if (is_dir($fullpath."/".$file)) {
-            $dirlist[] = $file;
-        } else {
-            $filelist[] = $file;
-        }
-    }
-    closedir($directory);
-
-    $strname = get_string("name");
-    $strsize = get_string("size");
-    $strmodified = get_string("modified");
-    $straction = get_string("action");
-    $strmakeafolder = get_string("makeafolder");
-    $struploadafile = get_string("uploadafile");
-    $strselectall = get_string("selectall");
-    $strselectnone = get_string("deselectall");
-    $strwithchosenfiles = get_string("withchosenfiles");
-    $strmovetoanotherfolder = get_string("movetoanotherfolder");
-    $strmovefilestohere = get_string("movefilestohere");
-    $strdeletecompletely = get_string("deletecompletely");
-    $strcreateziparchive = get_string("createziparchive");
-    $strrename = get_string("rename");
-    $stredit   = get_string("edit");
-    $strunzip  = get_string("unzip");
-    $strlist   = get_string("list");
-    $strrestore= get_string("restore");
-    $strchoose = get_string("choose");
-    $strfolder = get_string("folder");
-    $strfile   = get_string("file");
-
-
-    echo "<form action=\"index.php\" method=\"post\" id=\"dirform\">";
     echo "<div>";
-    echo '<input type="hidden" name="choose" value="'.$choose.'" />';
-    // echo "<hr align=\"center\" noshade=\"noshade\" size=\"1\" />";
     echo "<hr/>";
-    echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"2\" width=\"640\" class=\"files\">";
+    echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"2\" width=\"740\" class=\"files\">";
     echo "<tr>";
     echo "<th class=\"header\" scope=\"col\"></th>";
     echo "<th class=\"header name\" scope=\"col\">$strname</th>";
@@ -754,175 +226,86 @@ function displaydir ($wdir) {
     echo "<th class=\"header commands\" scope=\"col\">$straction</th>";
     echo "</tr>\n";
 
-    if ($wdir != "/") {
-        $dirlist[] = '..';
-    }
-
-    $count = 0;
-
-    if (!empty($dirlist)) {
-        asort($dirlist);
-        foreach ($dirlist as $dir) {
-            echo "<tr class=\"folder\">";
+    $parentwritable = $file_info->is_writable();
 
-            if ($dir == '..') {
-                $fileurl = rawurlencode(dirname($wdir));
-                print_cell();
-                // alt attribute intentionally empty to prevent repetition in screen reader
-                print_cell('left', '<a href="index.php?id='.$id.'&amp;wdir='.$fileurl.'&amp;choose='.$choose.'"><img src="'.$CFG->pixpath.'/f/parent.gif" class="icon" alt="" />&nbsp;'.get_string('parentfolder').'</a>', 'name');
-                print_cell();
-                print_cell();
-                print_cell();
+    if ($parent_info) {
+        $params = $parent_info->get_params_rawencoded();
+        $params = implode('&amp;', $params);
 
-            } else {
-                $count++;
-                $filename = $fullpath."/".$dir;
-                $fileurl  = rawurlencode($wdir."/".$dir);
-                $filesafe = rawurlencode($dir);
-                $filesize = display_size(get_directory_size("$fullpath/$dir"));
-                $filedate = userdate(filemtime($filename), "%d %b %Y, %I:%M %p");
-                print_cell("center", "<input type=\"checkbox\" name=\"file$count\" value=\"$fileurl\" />", 'checkbox');
-                print_cell("left", "<a href=\"index.php?id=$id&amp;wdir=$fileurl&amp;choose=$choose\"><img src=\"$CFG->pixpath/f/folder.gif\" class=\"icon\" alt=\"$strfolder\" />&nbsp;".htmlspecialchars($dir)."</a>", 'name');
-                print_cell("right", $filesize, 'size');
-                print_cell("right", $filedate, 'date');
-                print_cell("right", "<a href=\"index.php?id=$id&amp;wdir=$wdir&amp;file=$filesafe&amp;action=rename&amp;choose=$choose\">$strrename</a>", 'commands');
-            }
+        echo "<tr class=\"folder\">";
+        print_cell();
+        print_cell('left', '<a href="index.php?'.$params.'"><img src="'.$CFG->pixpath.'/f/parent.gif" class="icon" alt="" />&nbsp;'.get_string('parentfolder').'</a>', 'name');
+        print_cell();
+        print_cell();
+        print_cell();
 
-            echo "</tr>";
-        }
+        echo "</tr>";
     }
 
+    if ($children) {
+        foreach ($children as $child_info) {
+            $filename = $child_info->get_visible_name();
+            $filesize = $child_info->get_filesize();
+            $filesize = $filesize ? display_size($filesize) : '';
+            $filedate = $child_info->get_timemodified();
+            $filedate = $filedate ? userdate($filedate) : '';
 
-    if (!empty($filelist)) {
-        asort($filelist);
-        foreach ($filelist as $file) {
-
-            $icon = mimeinfo("icon", $file);
+            $mimetype = $child_info->get_mimetype();
 
-            $count++;
-            $filename    = $fullpath."/".$file;
-            $fileurl     = trim($wdir, "/")."/$file";
-            $filesafe    = rawurlencode($file);
-            $fileurlsafe = rawurlencode($fileurl);
-            $filedate    = userdate(filemtime($filename), "%d %b %Y, %I:%M %p");
+            $params = $child_info->get_params_rawencoded();
+            $params = implode('&amp;', $params);
 
-            $selectfile = trim($fileurl, "/");
+            if ($child_info->is_directory()) {
 
-            echo "<tr class=\"file\">";
+                echo "<tr class=\"folder\">";
+                print_cell();
+                print_cell("left", "<a href=\"index.php?$params\"><img src=\"$CFG->pixpath/f/folder.gif\" class=\"icon\" alt=\"$strfolder\" />&nbsp;".s($filename)."</a>", 'name');
+                print_cell("right", $filesize, 'size');
+                print_cell("right", $filedate, 'date');
+                if ($parentwritable) {
+                    print_cell("right", "<a href=\"index.php?$params&amp;sesskey=".sesskey()."&amp;delete=1\"><img src=\"$CFG->pixpath/t/delete.gif\" class=\"iconsmall\" alt=\"$strdelete\" /></a>", 'command');
+                } else {
+                    print_cell();
+                }
+                echo "</tr>";
 
-            print_cell("center", "<input type=\"checkbox\" name=\"file$count\" value=\"$fileurl\" />", 'checkbox');
-            echo "<td align=\"left\" style=\"white-space:nowrap\" class=\"name\">";
+            } else {
 
-            $ffurl = get_file_url($id.'/'.$fileurl);
-            link_to_popup_window ($ffurl, "display",
-                                  "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"$strfile\" />&nbsp;".htmlspecialchars($file),
-                                  480, 640);
-            echo "</td>";
+                $icon = mimeinfo_from_type("icon", $mimetype);
+                if ($downloadurl = $child_info->get_url(true)) {
+                    $downloadurl = "&nbsp;<a href=\"$downloadurl\"><img src=\"$CFG->pixpath/t/down.gif\" class=\"iconsmall\" alt=\"$strdownload\" />";
+                } else {
+                    $downloadurl = '';
+                }
 
-            $file_size = filesize($filename);
-            print_cell("right", display_size($file_size), 'size');
-            print_cell("right", $filedate, 'date');
+                if ($viewurl = $child_info->get_url()) {
+                    $viewurl = "&nbsp;".link_to_popup_window ($viewurl, "display",
+                                                     "<img src=\"$CFG->pixpath/t/preview.gif\" class=\"iconsmall\" alt=\"$strfile\" />&nbsp;",
+                                                     480, 640, null, null, true);
+                } else {
+                    $viewurl = '';
+                }
 
-            if ($choose) {
-                $edittext = "<strong><a onclick=\"return set_value('$selectfile')\" href=\"#\">$strchoose</a></strong>&nbsp;";
-            } else {
-                $edittext = '';
-            }
 
 
-            if ($icon == "text.gif" || $icon == "html.gif") {
-                $edittext .= "<a href=\"index.php?id=$id&amp;wdir=$wdir&amp;file=$fileurl&amp;action=edit&amp;choose=$choose\">$stredit</a>";
-            } else if ($icon == "zip.gif") {
-                $edittext .= "<a href=\"index.php?id=$id&amp;wdir=$wdir&amp;file=$fileurl&amp;action=unzip&amp;sesskey=$USER->sesskey&amp;choose=$choose\">$strunzip</a>&nbsp;";
-                $edittext .= "<a href=\"index.php?id=$id&amp;wdir=$wdir&amp;file=$fileurl&amp;action=listzip&amp;sesskey=$USER->sesskey&amp;choose=$choose\">$strlist</a> ";
-                if (!empty($CFG->backup_version) and has_capability('moodle/site:restore', get_context_instance(CONTEXT_COURSE, $id))) {
-                    $edittext .= "<a href=\"index.php?id=$id&amp;wdir=$wdir&amp;file=$filesafe&amp;action=restore&amp;sesskey=$USER->sesskey&amp;choose=$choose\">$strrestore</a> ";
+                echo "<tr class=\"file\">";
+                print_cell();
+                print_cell("left", "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"$strfile\" />&nbsp;".s($filename).$downloadurl.$viewurl, 'name');
+                print_cell("right", $filesize, 'size');
+                print_cell("right", $filedate, 'date');
+                if ($parentwritable) {
+                    print_cell("right", "<a href=\"index.php?$params&amp;sesskey=".sesskey()."&amp;delete=1\"><img src=\"$CFG->pixpath/t/delete.gif\" class=\"iconsmall\" alt=\"$strdelete\" /></a>", 'command');
+                } else {
+                    print_cell();
                 }
+                echo "</tr>";
             }
-
-            print_cell("right", "$edittext <a href=\"index.php?id=$id&amp;wdir=$wdir&amp;file=$filesafe&amp;action=rename&amp;choose=$choose\">$strrename</a>", 'commands');
-
-            echo "</tr>";
         }
     }
-    echo "</table>";
-    echo "<hr />";
-    //echo "<hr width=\"640\" align=\"center\" noshade=\"noshade\" size=\"1\" />";
-
-    echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"2\" width=\"640\">";
-    echo "<tr><td>";
-    echo "<input type=\"hidden\" name=\"id\" value=\"$id\" />";
-    echo '<input type="hidden" name="choose" value="'.$choose.'" />';
-    echo "<input type=\"hidden\" name=\"wdir\" value=\"$wdir\" /> ";
-    echo "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-    $options = array (
-                   "move" => "$strmovetoanotherfolder",
-                   "delete" => "$strdeletecompletely",
-                   "zip" => "$strcreateziparchive"
-               );
-    if (!empty($count)) {
-
-        choose_from_menu ($options, "action", "", "$strwithchosenfiles...", "javascript:getElementById('dirform').submit()");
-        echo '<div id="noscriptgo" style="display: inline;">';
-        echo '<input type="submit" value="'.get_string('go').'" />';
-        echo '<script type="text/javascript">'.
-               "\n//<![CDATA[\n".
-               'document.getElementById("noscriptgo").style.display = "none";'.
-               "\n//]]>\n".'</script>';
-        echo '</div>';
 
-    }
-    echo "</td></tr></table>";
-    echo "</div>";
-    echo "</form>";
-    echo "<table border=\"0\" cellspacing=\"2\" cellpadding=\"2\" width=\"640\"><tr>";
-    echo "<td align=\"center\">";
-    if (!empty($USER->fileop) and ($USER->fileop == "move") and ($USER->filesource <> $wdir)) {
-        echo "<form action=\"index.php\" method=\"get\">";
-        echo "<div>";
-        echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-        echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-        echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-        echo " <input type=\"hidden\" name=\"action\" value=\"paste\" />";
-        echo " <input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
-        echo " <input type=\"submit\" value=\"$strmovefilestohere\" />";
-        echo "</div>";
-        echo "</form>";
-    }
-    echo "</td>";
-    echo "<td align=\"right\">";
-        echo "<form action=\"index.php\" method=\"get\">";
-        echo "<div>";
-        echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-        echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-        echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-        echo " <input type=\"hidden\" name=\"action\" value=\"makedir\" />";
-        echo " <input type=\"submit\" value=\"$strmakeafolder\" />";
-        echo "</div>";
-        echo "</form>";
-    echo "</td>";
-    echo "<td align=\"right\">";
-        echo "<form action=\"index.php\" method=\"get\">"; //dummy form - alignment only
-        echo "<fieldset class=\"invisiblefieldset\">";
-        echo " <input type=\"button\" value=\"$strselectall\" onclick=\"checkall();\" />";
-        echo " <input type=\"button\" value=\"$strselectnone\" onclick=\"uncheckall();\" />";
-        echo "</fieldset>";
-        echo "</form>";
-    echo "</td>";
-    echo "<td align=\"right\">";
-        echo "<form action=\"index.php\" method=\"get\">";
-        echo "<div>";
-        echo ' <input type="hidden" name="choose" value="'.$choose.'" />';
-        echo " <input type=\"hidden\" name=\"id\" value=\"$id\" />";
-        echo " <input type=\"hidden\" name=\"wdir\" value=\"$wdir\" />";
-        echo " <input type=\"hidden\" name=\"action\" value=\"upload\" />";
-        echo " <input type=\"submit\" value=\"$struploadafile\" />";
-        echo "</div>";
-        echo "</form>";
-    echo "</td></tr>";
     echo "</table>";
+    echo "</div>";
     echo "<hr/>";
-    //echo "<hr width=\"640\" align=\"center\" noshade=\"noshade\" size=\"1\" />";
 
 }
 
index b75326a..27eecbd 100644 (file)
@@ -77,11 +77,11 @@ if ($editform->is_cancelled()) {
 } elseif ($data = $editform->get_data()) {
 
     if ($data->id) {
-        if (!groups_update_group($data, $editform->_upload_manager)) {
+        if (!groups_update_group($data, $editform)) {
             print_error('cannotupdategroup');
         }
     } else {
-        if (!$id = groups_create_group($data, $editform->_upload_manager)) {
+        if (!$id = groups_create_group($data, $editform)) {
             print_error('cannotcreategroup');
         }
         $returnurl = $CFG->wwwroot.'/group/index.php?id='.$course->id.'&amp;group='.$id;
index 9a96050..14c5685 100644 (file)
@@ -28,7 +28,6 @@ class group_form extends moodleform {
             $options = array(get_string('no'), get_string('yes'));
             $mform->addElement('select', 'hidepicture', get_string('hidepicture'), $options);
 
-            $this->set_upload_manager(new upload_manager('imagefile', false, false, null, false, 0, true, true, false));
             $mform->addElement('file', 'imagefile', get_string('newpicture', 'group'));
             $mform->setHelpButton('imagefile', array ('picture', get_string('helppicture')), true);
         }
index af3411f..23e3598 100644 (file)
@@ -90,7 +90,7 @@ function groups_remove_member($groupid, $userid) {
  * @param object $um upload manager with group picture
  * @return id of group or false if error
  */
-function groups_create_group($data, $um=false) {
+function groups_create_group($data, $editform=false) {
     global $CFG, $DB;
     require_once("$CFG->libdir/gdlib.php");
 
@@ -101,9 +101,9 @@ function groups_create_group($data, $um=false) {
 
     if ($id) {
         $data->id = $id;
-        if ($um) {
+        if ($editform) {
             //update image
-            if (save_profile_image($id, $um, 'groups')) {
+            if (save_profile_image($id, $editform, 'groups')) {
                 $DB->set_field('groups', 'picture', 1, array('id'=>$id));
             }
             $data->picture = 1;
@@ -144,7 +144,7 @@ function groups_create_grouping($data) {
  * @param object $um upload manager with group picture
  * @return boolean success
  */
-function groups_update_group($data, $um=false) {
+function groups_update_group($data, $editform=false) {
     global $CFG, $DB;
     require_once("$CFG->libdir/gdlib.php");
 
@@ -153,9 +153,9 @@ function groups_update_group($data, $um=false) {
     $result = $DB->update_record('groups', $data);
 
     if ($result) {
-        if ($um) {
+        if ($editform) {
             //update image
-            if (save_profile_image($data->id, $um, 'groups')) {
+            if (save_profile_image($data->id, $editform, 'groups')) {
             $DB->set_field('groups', 'picture', 1, array('id'=>$data->id));
                 $data->picture = 1;
             }
index 70240f6..27dc40d 100644 (file)
@@ -223,6 +223,7 @@ $string['guestnocomment'] = 'Guests are not allowed to post comments!';
 $string['guestnoeditprofile'] = 'The guest user cannot edit their profile';
 $string['guestnorate'] = 'Guests are not allowed to rate entries';
 $string['guestnoeditprofileother'] = 'The guest user profile cannot be edited';
+$string['hashpoolproblem'] = 'Incorrect pool file content $a.';
 $string['invalidaction'] = 'Invalid action parameter';
 $string['invalidarguments'] = 'No valid arguments supplied';
 $string['invalidargorconf'] = 'No valid arguments supplied or incorrect server configuration';
@@ -290,6 +291,10 @@ $string['listcantmoveup'] = 'Failed to move the item up, as it is the first of i
 $string['listcantmovedown'] = 'Failed to move item down, as it is the last of it\'s peers';
 $string['listcantmoveleft'] = 'Failed to move item left, as it has no parent';
 $string['listcantmoveright'] = 'Failed to move item right, as there is no peer to make it a child of. Move it below another peer and then you can move it right.';
+$string['localfilecannotcreatefiledirs'] = 'Can not create local file pool directories, please verify permissions in dataroot.';
+$string['localfilecannotread'] = 'Can not read file, either file does not exist or there are permission problems';
+$string['localfilenotcreated'] = 'Can not create file \"$a->contextid/$a->filearea/$a->itemid/$a->filepath/$a->filename\"';
+$string['localfileproblem'] = 'Unknown exception related to local files ($a)';
 $string['loginasonecourse'] = 'You cannot enter this course.<br /> You have to terminate the \"Login as\" session before entering any other course.';
 $string['loginasnoenrol'] = 'You cannot use enrol or unenrol when in course \"Login as\" session';
 $string['logfilenotavailable'] = 'Logs not available';
index 95c4c53..aa12f54 100644 (file)
@@ -168,6 +168,34 @@ $moodle_capabilities = array(
         )
     ),
 
+    'moodle/site:backupdownload' => array(
+
+        'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS,
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'editingteacher' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        ),
+
+        'clonepermissionsfrom' =>  'moodle/site:backup'
+    ),
+
+    'moodle/site:backupupload' => array(
+
+        'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS,
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'editingteacher' => CAP_ALLOW,
+            'admin' => CAP_ALLOW
+        ),
+
+        'clonepermissionsfrom' =>  'moodle/site:restore'
+    ),
+
     'moodle/site:restore' => array(
 
         'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS,
index 8ee33f2..0154ff5 100644 (file)
         <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="primary key of the table, please edit me"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="portfolio_log" COMMENT="log of portfolio transfers (used to later check for duplicates)" PREVIOUS="message_working">
+    <TABLE NAME="portfolio_log" COMMENT="log of portfolio transfers (used to later check for duplicates)" PREVIOUS="message_working" NEXT="files">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="userid"/>
         <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="user who exported content" PREVIOUS="id" NEXT="time"/>
         <KEY NAME="portfoliofk" TYPE="foreign" FIELDS="portfolio" REFTABLE="portfolio_instance" REFFIELDS="id" COMMENT="fk to portfolio_instance" PREVIOUS="userfk"/>
       </KEYS>
     </TABLE>
+    <TABLE NAME="files" COMMENT="description of files, content stored in sha1 file pool" PREVIOUS="portfolio_log" NEXT="files_cleanup">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="contenthash"/>
+        <FIELD NAME="contenthash" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="sha1 hash of file content" PREVIOUS="id" NEXT="pathnamehash"/>
+        <FIELD NAME="pathnamehash" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="complete file path sha1 hash - unique for each file" PREVIOUS="contenthash" NEXT="contextid"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="The context id defined in context table - identifies the instance of plugin owning the file" PREVIOUS="pathnamehash" NEXT="filearea"/>
+        <FIELD NAME="filearea" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="Like &quot;coursefiles&quot;. &quot;submission&quot;, &quot;intro&quot; and &quot;content&quot; (images and swf linked from summaries), etc." PREVIOUS="contextid" NEXT="itemid"/>
+        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="Optional - some plugin specific item id (eg. forum post, blog entry or assignment submission, user id for user files)" PREVIOUS="filearea" NEXT="filepath"/>
+        <FIELD NAME="filepath" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="Optional - relative path to file from module content root, useful in Scorm and Resource mod - most of the mods do not need this" PREVIOUS="itemid" NEXT="filename"/>
+        <FIELD NAME="filename" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="The full Unicode name of this file (case sensitive) - some chars are not allowed though" PREVIOUS="filepath" NEXT="userid"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="Optional - general userid field - meaning depending on plugin" PREVIOUS="filename" NEXT="filesize"/>
+        <FIELD NAME="filesize" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" PREVIOUS="userid" NEXT="mimetype"/>
+        <FIELD NAME="mimetype" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" ENUM="false" COMMENT="type of file - jpeg image, open document spreadsheet" PREVIOUS="filesize" NEXT="status"/>
+        <FIELD NAME="status" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="number greater than 0 means something is wrong with this file (virus, missing, etc.)" PREVIOUS="mimetype" NEXT="timecreated"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" PREVIOUS="status" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" PREVIOUS="timecreated"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="contextid"/>
+        <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id" PREVIOUS="primary" NEXT="userid"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="contextid"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="filearea-contextid-itemid" UNIQUE="false" FIELDS="filearea, contextid, itemid" NEXT="contenthash"/>
+        <INDEX NAME="contenthash" UNIQUE="false" FIELDS="contenthash" PREVIOUS="filearea-contextid-itemid" NEXT="pathnamehash"/>
+        <INDEX NAME="pathnamehash" UNIQUE="true" FIELDS="pathnamehash" PREVIOUS="contenthash"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="files_cleanup" COMMENT="File pool cleanup candidates" PREVIOUS="files">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="contenthash"/>
+        <FIELD NAME="contenthash" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="id"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="contenthash" UNIQUE="true" FIELDS="contenthash"/>
+      </INDEXES>
+    </TABLE>
   </TABLES>
   <STATEMENTS>
     <STATEMENT NAME="insert mnet_application" TYPE="insert" TABLE="mnet_application" COMMENT="Initial insert of records on table mnet_application" NEXT="insert log_display">
index 870fcba..d3cfa03 100644 (file)
@@ -264,13 +264,26 @@ function xmldb_main_upgrade($oldversion=0) {
     }
     
     if ($result && $oldversion < 2008072400) {
-        /// Create the database tables for message_processors 
+        /// Create the database tables for message_processors and message_providers
+        $table = new xmldb_table('message_providers');
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
+        $table->add_field('modulename', XMLDB_TYPE_CHAR, '166', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('modulefile', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $dbman->create_table($table);
+
         $table = new xmldb_table('message_processors');
         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
         $table->add_field('name', XMLDB_TYPE_CHAR, '166', null, XMLDB_NOTNULL, null, null, null, null);
         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
         $dbman->create_table($table);
 
+
+        $provider = new object();
+        $provider->modulename  = 'moodle';
+        $provider->modulefile  = 'index.php';
+        $DB->insert_record('message_providers', $provider);
+
     /// delete old and create new fields
         $table = new xmldb_table('message');
         $field = new xmldb_field('messagetype');
@@ -465,12 +478,78 @@ function xmldb_main_upgrade($oldversion=0) {
 
         upgrade_main_savepoint($result, 2008073104);
     }
-/*
- * TODO:
- *   drop adodb_logsql table and create a new general sql log table
- *
- */
+
+    if ($result && $oldversion < 2008073111) {
+    /// Define table files to be created
+        $table = new xmldb_table('files');
+
+    /// Adding fields to table files
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
+        $table->add_field('contenthash', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('pathnamehash', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('filearea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('filepath', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('filename', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null);
+        $table->add_field('filesize', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('mimetype', XMLDB_TYPE_CHAR, '100', null, null, null, null, null, null);
+        $table->add_field('status', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
+
+    /// Adding keys to table files
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+    /// Adding indexes to table files
+        $table->add_index('filearea-contextid-itemid', XMLDB_INDEX_NOTUNIQUE, array('filearea', 'contextid', 'itemid'));
+        $table->add_index('contenthash', XMLDB_INDEX_NOTUNIQUE, array('contenthash'));
+        $table->add_index('pathnamehash', XMLDB_INDEX_UNIQUE, array('pathnamehash'));
+
+    /// Conditionally launch create table for files
+        $dbman->create_table($table);
+
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2008073111);
+    }
+
+    if ($result && $oldversion < 2008073112) {
+    /// Define table files_cleanup to be created
+        $table = new xmldb_table('files_cleanup');
+
+    /// Adding fields to table files_cleanup
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
+        $table->add_field('contenthash', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null, null, null);
+
+    /// Adding keys to table files_cleanup
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+    /// Adding indexes to table files_cleanup
+        $table->add_index('contenthash', XMLDB_INDEX_UNIQUE, array('contenthash'));
+
+    /// Conditionally launch create table for files_cleanup
+        $dbman->create_table($table);
+
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2008073112);
+    }
+
+    if ($result && $oldversion < 2008073113) {
+    /// move all course, backup and other files to new filepool based storage
+        upgrade_migrate_files_courses();
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2008073113);
+    }
+
+    if ($result && $oldversion < 2008073114) {
+    /// move all course, backup and other files to new filepool based storage
+        upgrade_migrate_files_blog();
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2008073114);
+    }
 
     return $result;
 }
index 26d6537..cf08fea 100644 (file)
@@ -55,4 +55,169 @@ function upgrade_fix_category_depths() {
     }
 }
 
-?>
+/**
+ * Moves all course files except the moddata to new file storage
+ *
+ * Unfortunately this function uses core file related functions - it might be necessary to tweak it if something changes there :-(
+ */
+function upgrade_migrate_files_courses() {
+    global $DB, $CFG;
+    require_once($CFG->libdir.'/filelib.php');
+
+    $count = $DB->count_records('course');
+    $pbar = new progress_bar('migratecoursefiles', 500, true);
+
+    $rs = $DB->get_recordset('course');
+    $olddebug = $DB->get_debug();
+    $DB->set_debug(false); // lower debug level, there might be many files
+    $i = 0;
+    foreach ($rs as $course) {
+        $i++;
+        $context = get_context_instance(CONTEXT_COURSE, $course->id);
+        upgrade_migrate_files_course($context, '/', true);
+        $pbar->update($i, $count, "Migrated course files - course $i/$count.");
+    }
+    $DB->set_debug($olddebug); // reset debug level
+    $rs->close();
+
+    return true;
+}
+
+/**
+ * Internal function - do not use directly
+ */
+function upgrade_migrate_files_course($context, $path, $delete) {
+    global $CFG;
+
+    $fullpathname = $CFG->dataroot.'/'.$context->instanceid.$path;
+    if (!file_exists($fullpathname)) {
+        return;
+    }
+    $items = new DirectoryIterator($fullpathname);
+    $fs = get_file_storage();
+
+    foreach ($items as $item) {
+        if ($item->isDot()) {
+            continue;
+        }
+
+        if ($item->isLink()) {
+            // do not delete symbolic links or its children 
+            $delete_this = false;
+        } else {
+            $delete_this = $delete;
+        }
+
+        if (strpos($path, '/backupdata/') === 0) {
+            $filearea = 'course_backup';
+            $filepath = substr($path, strlen('/backupdata'));
+        } else {
+            $filearea = 'course_content';
+            $filepath = $path;
+        }
+
+        if ($item->isFile()) {
+            if (!$item->isReadable()) {
+                notify(" File not readable, skipping: ".$fullpathname.$item->getFilename());
+                continue;
+            }
+
+            $filepath = clean_param($filepath, PARAM_PATH);
+            $filename = clean_param($item->getFilename(), PARAM_FILE);
+
+            if ($filename === '') {
+                continue;
+            }
+
+            if (!$fs->file_exists($context->id, $filearea, '0', $filepath, $filename)) {
+                $file_record = array('contextid'=>$context->id, 'filearea'=>$filearea, 'itemid'=>0, 'filepath'=>$filepath, 'filename'=>$filename,
+                                     'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime());
+                if ($fs->create_file_from_pathname($file_record, $fullpathname.$item->getFilename())) {
+                    if ($delete_this) {
+                        @unlink($fullpathname.$item->getFilename());
+                    }
+                }
+            }
+
+        } else {
+            if ($path == '/' and $item->getFilename() == 'moddata') {
+                continue; // modules are responsible
+            }
+
+            $filepath = clean_param($filepath.$item->getFilename().'/', PARAM_PATH);
+            if ($filepath !== '/backupdata/') {
+                $fs->create_directory($context->id, $filearea, 0, $filepath);
+            }
+
+            //migrate recursively all subdirectories
+            upgrade_migrate_files_course($context, $path.$item->getFilename().'/', $delete_this);
+            if ($delete_this) {
+                // delete dir if empty
+                @rmdir($fullpathname.$item->getFilename());
+            }
+        }
+    }
+    unset($items); //release file handles
+}
+
+/**
+ * Moves all block attachments
+ *
+ * Unfortunately this function uses core file related functions - it might be necessary to tweak it if something changes there :-(
+ */
+function upgrade_migrate_files_blog() {
+    global $DB, $CFG;
+
+    $fs = get_file_storage();
+
+    $count = $DB->count_records_select('post', "module='blog' AND attachment IS NOT NULL AND attachment <> 1");
+
+    if ($rs = $DB->get_recordset_select('post', "module='blog' AND attachment IS NOT NULL AND attachment <> 1")) {
+
+        $pbar = new progress_bar('migrateblogfiles', 500, true);
+
+        $olddebug = $DB->get_debug();
+        $DB->set_debug(false); // lower debug level, there might be many files
+        $i = 0;
+        foreach ($rs as $entry) {
+            $i++;
+            $pathname = "$CFG->dataroot/blog/attachments/$entry->id/$entry->attachment";
+            if (!file_exists($pathname)) {
+                // hmm, we could set atatchment NULL here, but it would break badly in concurrent ugprades, disabling for now
+                //$entry->attachment = NULL;
+                //$DB->update_record('post', $entry);
+                continue;
+            }
+
+            $filename = clean_param($entry->attachment, PARAM_FILE);
+            if ($filename === '') {
+                // weird file name, ignore it
+                $entry->attachment = NULL;
+                $DB->update_record('post', $entry);
+                continue;
+            }
+
+            if (!is_readable($pathname)) {
+                notify(" File not readable, skipping: ".$pathname);
+                continue;
+            }
+
+            if (!$fs->file_exists(SYSCONTEXTID, 'blog', $entry->id, '/', $filename)) {
+                $file_record = array('contextid'=>SYSCONTEXTID, 'filearea'=>'blog', 'itemid'=>$entry->id, 'filepath'=>'/', 'filename'=>$filename,
+                                     'timecreated'=>filectime($pathname), 'timemodified'=>filemtime($pathname), 'userid'=>$post->userid);
+                $fs->create_file_from_pathname($file_record, $pathname);
+            }
+            @unlink($pathname);
+            @rmdir("$CFG->dataroot/blog/attachments/$entry->id/");
+
+            $entry->attachment = 1; // file name not needed there anymore
+            $DB->update_record('post', $entry);
+            $pbar->update($i, $count, "Migrated blog attachments - $i/$count.");
+        }
+        $DB->set_debug($olddebug); // reset debug level
+        $rs->close();
+    }
+    
+    @rmdir("$CFG->dataroot/blog/attachments/");
+    @rmdir("$CFG->dataroot/blog/");
+}
index f1ea8f2..bb9d928 100644 (file)
@@ -14,6 +14,8 @@
     require("../../../config.php");
     require_once($CFG->libdir.'/filelib.php');
 
+error('Not reimplemented yet, sorry');
+
     $id      = required_param('id', PARAM_INT);
     $file    = optional_param('file', '', PARAM_PATH);
     $wdir    = optional_param('wdir', '', PARAM_PATH);
index d8dc132..1faf836 100644 (file)
@@ -14,6 +14,8 @@
     require("../../../config.php");\r
     require_once($CFG->libdir.'/filelib.php');\r
 \r
+error('Not reimplemented yet, sorry');\r
+\r
     $id      = required_param('id', PARAM_INT);\r
     $file    = optional_param('file', '', PARAM_PATH);\r
     $wdir    = optional_param('wdir', '', PARAM_PATH);\r
diff --git a/lib/file/file_browser.php b/lib/file/file_browser.php
new file mode 100644 (file)
index 0000000..62d405c
--- /dev/null
@@ -0,0 +1,293 @@
+<?php  //$Id$
+
+require_once("$CFG->libdir/file/file_info.php");
+require_once("$CFG->libdir/file/file_info_stored.php");
+require_once("$CFG->libdir/file/file_info_system.php");
+require_once("$CFG->libdir/file/file_info_user.php");
+require_once("$CFG->libdir/file/file_info_coursecat.php");
+require_once("$CFG->libdir/file/file_info_course.php");
+require_once("$CFG->libdir/file/file_info_coursefile.php");
+
+/**
+ * Main interface for browsing of file tree (local files, areas, virtual files, etc.).
+ */
+class file_browser {
+
+    /**
+     * Looks up file_info object
+     * @param object $context
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $filepath
+     * @param string $filename
+     * @return object file_info object or null if not found or access not allowed
+     */
+    public function get_file_info($context, $filearea=null, $itemid=null, $filepath=null, $filename=null) {
+        global $USER, $CFG, $DB;
+
+        $fs = get_file_storage();
+
+        if ($context->contextlevel == CONTEXT_SYSTEM) {
+            if (is_null($filearea)) {
+                return new file_info_system($this);
+            }
+            //TODO: question files browsing
+
+        } else if ($context->contextlevel == CONTEXT_USER) {
+            // access control: only own files
+            if ($context->instanceid != $USER->id) {
+                return null;
+            }
+
+            if (!is_null($filearea) and !in_array($filearea, array('user_private', 'user_draft'))) {
+                // file area does not exist, sorry
+                return null;
+            }
+
+            if (is_null($filearea)) {
+                return new file_info_user($this, $context);
+            } else {
+                if ($filearea == 'user_private') {
+                    if (is_null($itemid)) {
+                        return new file_info_user($this, $context);
+                    }
+                    $filepath = is_null($filepath) ? '/' : $filepath;
+                    $filename = is_null($filename) ? '.' : $filename;
+
+                    if (!$localfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
+                        if ($filepath === '/' and $filename === '.') {
+                            $localfile = $fs->create_directory($context->id, $filearea, 0, $filepath, $USER->id);
+                        } else {
+                            // not found
+                            return null;
+                        }
+                    }
+                    $urlbase = $CFG->wwwroot.'/userfile.php';
+                    // TODO: localise
+                    return new file_info_stored($this, $context, $localfile, $urlbase, 'Personal files', false, true, true);
+
+                } else if ($filearea == 'user_draft') {
+                    if (empty($itemid)) {
+                        return new file_info_user($this, $context);
+                    }
+                    $urlbase = $CFG->wwwroot.'/draftfile.php';
+                    if (!$localfile = $fs->get_file($context->id, $filearea, $itemid, $filepath, $filename)) {
+                        return null;
+                    }
+                    //something must create the top most directory
+                    // TODO: localise
+                    return new file_info_stored($this, $context, $localfile, $urlbase, 'Draft file area', true, true, true);
+                }
+            }
+
+        } else if ($context->contextlevel == CONTEXT_COURSECAT) {
+            if (!$category = $DB->get_record('course_categories', array('id'=>$context->instanceid))) {
+                return null;
+            }
+
+            if (!$category->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                return null;
+            }
+
+            if (!is_null($filearea) and !in_array($filearea, array('coursecat_intro'))) {
+                // file area does not exist, sorry
+                $filearea = null;
+            }
+
+            if (is_null($filearea) or is_null($itemid)) {
+                return new file_info_coursecat($this, $context, $category);
+
+            } else {
+                if ($filearea == 'coursecat_intro') {
+                    if (!has_capability('moodle/course:update', $context)) {
+                        return null;
+                    }
+
+                    $filepath = is_null($filepath) ? '/' : $filepath;
+                    $filename = is_null($filename) ? '.' : $filename;
+
+                    $urlbase = $CFG->wwwroot.'/pluginfile.php';
+                    if (!$localfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
+                        if ($filepath === '/' and $filename === '.') {
+                            $localfile = $fs->create_directory($context->id, $filearea, 0, $filepath);
+                        } else {
+                            // not found
+                            return null;
+                        }
+                    }
+                    // TODO: localise
+                    return new file_info_stored($this, $context, $localfile, $urlbase, 'Category introduction files', false, true, true);
+
+                }
+            }
+
+        } else if ($context->contextlevel == CONTEXT_COURSE) {
+            if (!$course = $DB->get_record('course', array('id'=>$context->instanceid))) {
+                return null;
+            }
+
+            if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                return null;
+            }
+
+            if (!is_null($filearea) and !in_array($filearea, array('course_intro', 'course_content', 'course_backup'))) {
+                // file area does not exist, sorry
+                $filearea = null;
+            }
+
+            $filepath = is_null($filepath) ? '/' : $filepath;
+            $filename = is_null($filename) ? '.' : $filename;
+
+            if (is_null($filearea) or is_null($itemid)) {
+                return new file_info_course($this, $context, $course);
+
+            } else {
+                if ($filearea === 'course_intro') {
+                    if (!has_capability('moodle/course:update', $context)) {
+                        return null;
+                    }
+
+                    $urlbase = $CFG->wwwroot.'/pluginfile.php';
+                    if (!$localfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
+                        if ($filepath === '/' and $filename === '.') {
+                            $localfile = $fs->create_directory($context->id, $filearea, 0, $filepath);
+                        } else {
+                            // not found
+                            return null;
+                        }
+                    }
+                    // TODO: localise
+                    return new file_info_stored($this, $context, $localfile, $urlbase, 'Course introduction files', false, true, true);
+
+                } else if ($filearea == 'course_backup') {
+                    if (!has_capability('moodle/site:backup', $context) and !has_capability('moodle/site:restore', $context)) {
+                        return null;
+                    }
+
+                    $urlbase = $CFG->wwwroot.'/pluginfile.php';
+                    if (!$localfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
+                        if ($filepath === '/' and $filename === '.') {
+                            $localfile = $fs->create_directory($context->id, $filearea, 0, $filepath);
+                        } else {
+                            // not found
+                            return null;
+                        }
+                    }
+
+                    $downloadable = has_capability('moodle/site:backupdownload', $context);
+                    $uploadable   = has_capability('moodle/site:backupupload', $context);
+                    // TODO: localise
+                    return new file_info_stored($this, $context, $localfile, $urlbase, 'Backup files', false, $downloadable, $uploadable);
+
+                } else if ($filearea == 'course_content') {
+                    if (!has_capability('moodle/course:managefiles', $context)) {
+                        return null;
+                    }
+
+                    if (!$localfile = $fs->get_file($context->id, $filearea, 0, $filepath, $filename)) {
+                        if ($filepath === '/' and $filename === '.') {
+                            $localfile = $fs->create_directory($context->id, $filearea, 0, $filepath);
+                        } else {
+                            // not found
+                            return null;
+                        }
+                    }
+
+                    return new file_info_coursefile($this, $context, $localfile);
+                }
+            }
+
+        } else if ($context->contextlevel == CONTEXT_MODULE) {
+            //TODO
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns content of local directory
+     */
+    public function build_stored_file_children($context, $filearea, $itemid, $filepath, $urlbase, $areavisiblename, $itemidused, $readaccess, $writeaccess) {
+        global $DB;
+
+        $dirs = array();
+        $files = array();
+        $fs = get_file_storage();
+        $level = substr_count($filepath, '/');
+
+        // TODO: this should be improved ;-)
+        $localfiles = $fs->get_area_files($context->id, $filearea, $itemid, "filepath, filename");
+        foreach ($localfiles as $file) {
+            $name = $file->get_filename();
+            $path = $file->get_filepath();
+            $l = substr_count($path, '/');
+
+            if ($filepath === $path) {
+                if ($name !== '.') {
+                    $files[] = new file_info_stored($this, $context, $file, $urlbase, $areavisiblename, $itemidused, $readaccess, $writeaccess);
+                }
+            } else if ($level == $l-1 and $name === '.' and strpos($path, $filepath) === 0) {
+                $dirs[] = new file_info_stored($this, $context, $file, $urlbase, $areavisiblename, $itemidused, $readaccess, $writeaccess);
+            }
+        }
+
+        return array_merge($dirs, $files);
+    }
+
+    /**
+     * Returns content of coursefiles directory
+     */
+    public function build_coursefile_children($context, $filepath) {
+        global $DB;
+
+        $dirs = array();
+        $files = array();
+        $fs = get_file_storage();
+        $level = substr_count($filepath, '/');
+
+        // TODO: this should be improved ;-)
+        $localfiles = $fs->get_area_files($context->id, 'course_content', 0, "filepath, filename");
+        foreach ($localfiles as $file) {
+            $name = $file->get_filename();
+            $path = $file->get_filepath();
+            $l = substr_count($path, '/');
+
+            if ($filepath === $path) {
+                if ($name !== '.') {
+                    $files[] = new file_info_coursefile($this, $context, $file);
+                }
+            } else if ($level == $l-1 and $name === '.' and strpos($path, $filepath) === 0) {
+                $dirs[] = new file_info_coursefile($this, $context, $file);
+            }
+        }
+
+        return array_merge($dirs, $files);
+    }
+
+    public function encodepath($urlbase, $path, $forcedownload=false, $https=false) {
+        global $CFG;
+
+        if ($CFG->slasharguments) {
+            $parts = explode('/', $path);
+            $parts = array_map('rawurlencode', $parts);
+            $path  = implode('/', $parts);
+            $return = $urlbase.$path;
+            if ($forcedownload) {
+                $return .= '?forcedownload=1';
+            }
+        } else {
+            $path = rawurlencode($path);
+            $return = $urlbase.'?file='.$path;
+            if ($forcedownload) {
+                $return .= '&amp;forcedownload=1';
+            }
+        }
+
+        if ($https) {
+            $return = str_replace('http://', 'https://', $return);
+        }
+
+        return $return;
+    }
+
+}
diff --git a/lib/file/file_exceptions.php b/lib/file/file_exceptions.php
new file mode 100644 (file)
index 0000000..ab58bb2
--- /dev/null
@@ -0,0 +1,43 @@
+<?php  //$Id$
+
+/**
+ * Basic file related exception class
+ */
+class file_exception extends moodle_exception {
+    function __construct($errorcode, $a=NULL, $debuginfo=null) {
+        parent::__construct($errorcode, '', '', $a, $debuginfo);
+    }
+}
+
+/**
+ * Table does not exist problem exception
+ */
+class stored_file_creation_exception extends file_exception {
+    function __construct($contextid, $filearea, $itemid, $filepath, $filename, $debuginfo=null) {
+        $a = new object();
+        $a->contextid = $contextid;
+        $a->filearea  = $filearea;
+        $a->itemid    = $itemid;
+        $a->filepath  = $filepath;
+        $a->filename  = $filename;
+        parent::__construct('localfilenotcreated', $a, $debuginfo);
+    }
+}
+
+/**
+ * Table does not exist problem exception
+ */
+class file_access_exception extends file_exception {
+    function __construct($debuginfo=null) {
+        parent::__construct('nopermissions', NULL, $debuginfo);
+    }
+}
+
+/**
+ * Hash file content problem
+ */
+class file_pool_content_exception extends file_exception {
+    function __construct($contenthash, $debuginfo=null) {
+        parent::__construct('hashpoolproblem', $contenthash, $debuginfo);
+    }
+}
diff --git a/lib/file/file_info.php b/lib/file/file_info.php
new file mode 100644 (file)
index 0000000..495fe2c
--- /dev/null
@@ -0,0 +1,89 @@
+<?php  //$Id$
+
+abstract class file_info {
+    protected $context;
+    protected $browser;
+
+    public function __construct($browser, $context) {
+        $this->browser = $browser;
+        $this->context = $context;
+    }
+
+    public abstract function get_params();
+    public abstract function get_visible_name();
+    public abstract function is_directory();
+    public abstract function get_children();
+    public abstract function get_parent();
+
+
+    public function get_params_rawencoded() {
+        $params = $this->get_params();
+        $encoded = array();
+        $encoded[] = 'contextid='.$params['contextid'];
+        $encoded[] = 'filearea='.$params['filearea'];
+        $encoded[] = 'itemid='.(is_null($params['itemid']) ? -1 : $params['itemid']);
+        $encoded[] = 'filepath='.(is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
+        $encoded[] = 'filename='.((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
+
+        return $encoded;
+    }
+
+
+
+    public function get_url($forcedownload=false, $https=false) {
+        return null;
+    }
+
+    public function is_readable() {
+        return true;
+    }
+
+    public function is_writable() {
+        return true;
+    }
+
+    public function get_filesize() {
+        return null;
+    }
+
+    public function get_mimetype() {
+        // TODO: add some custom mime icons for courses, categories??
+        return null;
+    }
+
+    public function get_timecreated() {
+        return null;
+    }
+
+    public function get_timemodified() {
+        return null;
+    }
+
+    public function create_directory($newdirname, $userid=null) {
+        return null;
+    }
+
+    public function create_file_from_string($newfilename, $content, $userid=null) {
+        return null;
+    }
+
+    public function create_file_from_pathname($newfilename, $pathname, $userid=null) {
+        return null;
+    }
+
+    public function create_file_from_localfile($newfilename, $fid, $userid=null) {
+        return null;
+    }
+
+    public function delete() {
+        return false;
+    }
+
+//TODO: following methods are not implemented yet ;-)
+
+    //public abstract function copy(location params);
+    //public abstract function move(location params);
+    //public abstract function rename(new name);
+    //public abstract function unzip(location params);
+    //public abstract function zip(zip file, file info);
+}
diff --git a/lib/file/file_info_course.php b/lib/file/file_info_course.php
new file mode 100644 (file)
index 0000000..dd8bf79
--- /dev/null
@@ -0,0 +1,61 @@
+<?php  //$Id$
+
+class file_info_course extends file_info {
+    protected $course;
+
+    public function __construct($browser, $context, $course) {
+        global $DB;
+        parent::__construct($browser, $context);
+        $this->course   = $course;
+    }
+
+    public function get_params() {
+        return array('contextid'=>$this->context->id,
+                     'filearea' =>null,
+                     'itemid'   =>null,
+                     'filepath' =>null,
+                     'filename' =>null);
+    }
+
+    public function get_visible_name() {
+        return ($this->course->id == SITEID) ? get_string('frontpage', 'admin') : format_string($this->course->fullname);
+    }
+
+    public function is_writable() {
+        return false;
+    }
+
+    public function is_directory() {
+        return true;
+    }
+
+    public function get_children() {
+        $children = array();
+
+        if (has_capability('moodle/course:update', $this->context)) {
+            if ($child = $this->browser->get_file_info($this->context, 'course_intro', 0)) {
+                $children[] = $child;
+            }
+        }
+
+        if (has_capability('moodle/site:backup', $this->context) or has_capability('moodle/site:restorep', $this->context)) {
+            if ($child = $this->browser->get_file_info($this->context, 'course_backup', 0)) {
+                $children[] = $child;
+            }
+        }
+
+        if (has_capability('moodle/course:managefiles', $this->context)) {
+            if ($child = $this->browser->get_file_info($this->context, 'course_content', 0)) {
+                $children[] = $child;
+            }
+        }
+
+        return $children;
+    }
+
+    public function get_parent() {
+        $pcid = get_parent_contextid($this->context);
+        $parent = get_context_instance_by_id($pcid);
+        return $this->browser->get_file_info($parent);
+    }
+}
diff --git a/lib/file/file_info_coursecat.php b/lib/file/file_info_coursecat.php
new file mode 100644 (file)
index 0000000..15c2430
--- /dev/null
@@ -0,0 +1,66 @@
+<?php  //$Id$
+
+class file_info_coursecat extends file_info {
+    protected $category;
+
+    public function __construct($browser, $context, $category) {
+        parent::__construct($browser, $context);
+        $this->category = $category;
+    }
+
+    public function get_params() {
+        return array('contextid'=>$this->context->id,
+                     'filearea' =>null,
+                     'itemid'   =>null,
+                     'filepath' =>null,
+                     'filename' =>null);
+    }
+
+    public function get_visible_name() {
+        return format_string($this->category->name);
+    }
+
+    public function is_directory() {
+        return true;
+    }
+
+    public function get_children() {
+        global $DB;
+
+        $children = array();
+
+        if ($child = $this->browser->get_file_info($this->context, 'coursecat_intro', 0)) {
+            $children[] = $child;
+        }
+
+        $course_cats = $DB->get_records('course_categories', array('parent'=>$this->category->id), 'sortorder');
+        foreach ($course_cats as $category) {
+            $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
+            if (!$category->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                continue;
+            }
+            if ($child = $this->browser->get_file_info($context)) {
+                $children[] = $child;
+            }
+        }
+
+        $courses = $DB->get_records('course', array('category'=>$this->category->id), 'sortorder');
+        foreach ($courses as $course) {
+            $context = get_context_instance(CONTEXT_COURSE, $course->id);
+            if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                continue;
+            }
+            if ($child = $this->browser->get_file_info($context)) {
+                $children[] = $child;
+            }
+        }
+
+        return $children;
+    }
+
+    public function get_parent() {
+        $cid = get_parent_contextid($this->context);
+        $parent = get_context_instance_by_id($cid);
+        return $this->browser->get_file_info($parent);
+    }
+}
diff --git a/lib/file/file_info_coursefile.php b/lib/file/file_info_coursefile.php
new file mode 100644 (file)
index 0000000..d255362
--- /dev/null
@@ -0,0 +1,38 @@
+<?php  //$Id$
+
+class file_info_coursefile extends file_info_stored {
+    public function __construct($browser, $context, $localfile) {
+        global $CFG;
+        $urlbase = $CFG->wwwroot.'/file.php';
+        parent::__construct($browser, $context, $localfile, $urlbase, 'Course files', false, true, true); // TODO: localise
+    }
+
+    public function get_url($forcedownload=false, $https=false) {
+        global $CFG;
+
+        if (!$this->is_readable()) {
+            return null;
+        }
+
+        if ($this->lf->get_filename() === '.') {
+            return null;
+        }
+
+        $filepath = $this->lf->get_filepath();
+        $filename = $this->lf->get_filename();
+        $courseid = $this->context->instanceid;
+
+        $path = '/'.$courseid.$filepath.$filename;
+
+        return $this->browser->encodepath($this->urlbase, $path, $forcedownload, $https);
+    }
+
+    public function get_children() {
+        if ($this->lf->get_filename() !== '.') {
+            return array(); //not a dir
+        }
+        return $this->browser->build_coursefile_children($this->context, $this->lf->get_filepath());
+    }
+
+
+}
diff --git a/lib/file/file_info_stored.php b/lib/file/file_info_stored.php
new file mode 100644 (file)
index 0000000..9c2a98f
--- /dev/null
@@ -0,0 +1,278 @@
+<?php  //$Id$
+
+class file_info_stored extends file_info {
+    protected $lf;
+    protected $urlbase;
+    protected $areavisiblename;
+    protected $itemidused;
+    protected $readaccess;
+    protected $writeaccess;
+
+    public function __construct($browser, $context, $localfile, $urlbase, $areavisiblename, $itemidused, $readaccess, $writeaccess) {
+        parent::__construct($browser, $context);
+
+        $this->lf              = $localfile;
+        $this->urlbase         = $urlbase;
+        $this->areavisiblename = $areavisiblename;
+        $this->itemidused      = $itemidused;
+        $this->readaccess      = $readaccess;
+        $this->writeaccess     = $writeaccess;
+    }
+
+    public function get_params() {
+        return array('contextid'=>$this->context->id,
+                     'filearea' =>$this->lf->get_filearea(),
+                     'itemid'   =>$this->lf->get_itemid(),
+                     'filepath' =>$this->lf->get_filepath(),
+                     'filename' =>$this->lf->get_filename());
+    }
+
+    public function get_visible_name() {
+        $filename = $this->lf->get_filename();
+        $filepath = $this->lf->get_filepath();
+
+        if ($filename !== '.') {
+            return $filename;
+
+        } else {
+            $dir = trim($filepath, '/');
+            $dir = explode('/', $dir);
+            $dir = array_pop($dir);
+            if ($dir === '') {
+                if ($this->itemidused) {
+                    return $this->itemid;
+                } else {
+                    return $this->areavisiblename;
+                }
+            } else {
+                return $dir;
+            }
+        }
+    }
+
+    public function get_url($forcedownload=false, $https=false) {
+        global $CFG;
+
+        if (!$this->is_readable()) {
+            return null;
+        }
+
+        if ($this->is_directory()) {
+            return null;
+        }
+
+        $this->urlbase;
+        $contextid = $this->lf->get_contextid();
+        $filearea  = $this->lf->get_filearea();
+        $filepath  = $this->lf->get_filepath();
+        $filename  = $this->lf->get_filename();
+        $itemid    = $this->lf->get_itemid();
+
+        if ($this->itemidused) {
+            $path = '/'.$contextid.'/'.$filearea.'/'.$itemid.$filepath.$filename;
+        } else {
+            $path = '/'.$contextid.'/'.$filearea.$filepath.$filename;
+        }
+        return $this->browser->encodepath($this->urlbase, $path, $forcedownload, $https);
+    }
+
+    public function is_readable() {
+        return $this->readaccess;
+    }
+
+    public function is_writable() {
+        return $this->writeaccess;
+    }
+
+    public function get_filesize() {
+        return $this->lf->get_filesize();
+    }
+
+    public function get_mimetype() {
+        // TODO: add some custom mime icons for courses, categories??
+        return $this->lf->get_mimetype();
+    }
+
+    public function get_timecreated() {
+        return $this->lf->get_timecreated();
+    }
+
+    public function get_timemodified() {
+        return $this->lf->get_timemodified();
+    }
+
+    public function is_directory() {
+        if (!$this->lf) {
+            return true;
+        }
+
+        return ($this->lf->get_filename() === '.');
+    }
+
+    public function get_children() {
+        if ($this->lf->get_filename() !== '.') {
+            return array(); //not a dir
+        }
+        return $this->browser->build_stored_file_children($this->context, $this->lf->get_filearea(), $this->lf->get_itemid(), $this->lf->get_filepath(),
+                                                         $this->urlbase, $this->areavisiblename, $this->itemidused, $this->readaccess, $this->writeaccess);
+    }
+
+    public function get_parent() {
+        if ($this->lf->get_filename() !== '.') {
+            return $this->browser->get_file_info($this->context, $this->lf->get_filearea(), $this->lf->get_itemid(), $this->lf->get_filepath(), '.');
+        }
+
+        if ($this->lf->get_filepath() === '/') {
+            if ($this->itemidused) {
+                return $this->browser->get_file_info($this->context, $this->lf->get_filearea(), $this->lf->get_itemid());
+            } else {
+                return $this->browser->get_file_info($this->context, $this->lf->get_filearea());
+            }
+        }
+
+        $filepath = $this->lf->get_filepath();
+        $filepath = trim($filepath, '/');
+        $dirs = explode('/', $filepath);
+        array_pop($dirs);
+        $filepath = implode('/', $dirs);
+        $filepath = ($filepath === '') ? '/' : "/$filepath/";
+
+        return $this->browser->get_file_info($this->context, $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, '.');
+    }
+
+    public function create_directory($newdirname, $userid=null) {
+        if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
+            return null;
+        }
+
+        $newdirname = clean_param($newdirname, PARAM_FILE);
+        if ($newdirname === '') {
+            return null;
+        }
+
+        $filepath = $this->lf->get_filepath().'/'.$newdirname.'/';
+
+        $fs = get_file_storage();
+
+        if ($file = $fs->create_directory($this->lf->get_contextid(), $this->lf->get_filearea(), $this->lf->get_itemid(), $filepath, $userid)) {
+            return $this->browser->get_file_info($this->context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
+        }
+        return null;
+    }
+
+
+    public function create_file_from_string($newfilename, $content, $userid=null) {
+        if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
+            return null;
+        }
+
+        $newfilename = clean_param($newfilename, PARAM_FILE);
+        if ($newfilename === '') {
+            return null;
+        }
+
+        $now = time();
+
+        $newrecord = new object();
+        $newrecord->contextid = $this->lf->get_contextid();
+        $newrecord->filearea  = $this->lf->get_filearea();
+        $newrecord->itemid    = $this->lf->get_itemid();
+        $newrecord->filepath  = $this->lf->get_filepath();
+        $newrecord->filename  = $newfilename;
+
+        $newrecord->timecreated  = $now;
+        $newrecord->timemodified = $now;
+        $newrecord->mimetype     = mimeinfo('type', $newfilename);
+        $newrecord->userid       = $userid;
+
+        $fs = get_file_storage();
+
+        if ($file = $fs->create_file_from_string($newrecord, $content)) {
+            return $this->browser->get_file_info($this->context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
+        }
+        return null;
+    }
+
+    public function create_file_from_pathname($newfilename, $pathname, $userid=null) {
+        if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
+            return null;
+        }
+
+        $newfilename = clean_param($newfilename, PARAM_FILE);
+        if ($newfilename === '') {
+            return null;
+        }
+
+        $now = time();
+
+        $newrecord = new object();
+        $newrecord->contextid = $this->lf->get_contextid();
+        $newrecord->filearea  = $this->lf->get_filearea();
+        $newrecord->itemid    = $this->lf->get_itemid();
+        $newrecord->filepath  = $this->lf->get_filepath();
+        $newrecord->filename  = $newfilename;
+
+        $newrecord->timecreated  = $now;
+        $newrecord->timemodified = $now;
+        $newrecord->mimetype     = mimeinfo('type', $newfilename);
+        $newrecord->userid       = $userid;
+
+        $fs = get_file_storage();
+
+        if ($file = $fs->create_file_from_pathname($newrecord, $pathname)) {
+            return $this->browser->get_file_info($this->context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
+        }
+        return null;
+    }
+
+    public function create_file_from_localfile($newfilename, $fid, $userid=null) {
+        if (!$this->is_writable() or $this->lf->get_filename() !== '.') {
+            return null;
+        }
+
+        $newfilename = clean_param($newfilename, PARAM_FILE);
+        if ($newfilename === '') {
+            return null;
+        }
+
+        $now = time();
+
+        $newrecord = new object();
+        $newrecord->contextid = $this->lf->get_contextid();
+        $newrecord->filearea  = $this->lf->get_filearea();
+        $newrecord->itemid    = $this->lf->get_itemid();
+        $newrecord->filepath  = $this->lf->get_filepath();
+        $newrecord->filename  = $newfilename;
+
+        $newrecord->timecreated  = $now;
+        $newrecord->timemodified = $now;
+        $newrecord->mimetype     = mimeinfo('type', $newfilename);
+        $newrecord->userid       = $userid;
+
+        $fs = get_file_storage();
+
+        if ($file = $fs->create_file_from_localfile($newrecord, $fid)) {
+            return $this->browser->get_file_info($this->context, $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());
+        }
+        return null;
+    }
+
+    public function delete() {
+        if (!$this->lf or !$this->is_writable()) {
+            return false;
+        }
+
+        if ($this->is_directory()) {
+            $filepath = $this->lf->get_filepath();
+            $fs = get_file_storage();
+            $localfiles = $fs->get_area_files($this->context->id, $this->lf->get_filearea(), $this->lf->get_itemid(), "");
+            foreach ($localfiles as $file) {
+                if (strpos($file->get_filepath(), $filepath) === 0) {
+                    $file->delete();
+                }
+            }
+        }
+
+        return $this->lf->delete();
+    }
+}
diff --git a/lib/file/file_info_system.php b/lib/file/file_info_system.php
new file mode 100644 (file)
index 0000000..315ab17
--- /dev/null
@@ -0,0 +1,65 @@
+<?php  //$Id$
+
+class file_info_system extends file_info {
+    public function __construct($browser) {
+        parent::__construct($browser, get_context_instance(CONTEXT_SYSTEM));
+    }
+
+    public function get_params() {
+        return array('contextid'=>$this->context->id,
+                     'filearea' =>null,
+                     'itemid'   =>null,
+                     'filepath' =>null,
+                     'filename' =>null);
+    }
+
+    public function get_visible_name() {
+        return 'Root'; // TODO: fix & localise
+    }
+
+    public function is_writable() {
+        return false;
+    }
+
+    public function is_directory() {
+        return true;
+    }
+
+    public function get_children() {
+        global $DB, $USER;
+
+        $children = array();
+
+        if ($child = $this->browser->get_file_info(get_context_instance(CONTEXT_USER, $USER->id))) {
+            $children[] = $child;
+        }
+
+        $course_cats = $DB->get_records('course_categories', array('parent'=>0), 'sortorder');
+        foreach ($course_cats as $category) {
+            $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
+            if (!$category->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                continue;
+            }
+            if ($child = $this->browser->get_file_info($context)) {
+                $children[] = $child;
+            }
+        }
+
+        $courses = $DB->get_records('course', array('category'=>0), 'sortorder');
+        foreach ($courses as $course) {
+            if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                continue;
+            }
+            $context = get_context_instance(CONTEXT_COURSE, $course->id);
+            if ($child = $this->browser->get_file_info($context)) {
+                $children[] = $child;
+            }
+        }
+
+        return $children;
+    }
+
+    public function get_parent() {
+        return null;
+    }
+}
diff --git a/lib/file/file_info_user.php b/lib/file/file_info_user.php
new file mode 100644 (file)
index 0000000..7af4e30
--- /dev/null
@@ -0,0 +1,47 @@
+<?php  //$Id$
+
+class file_info_user extends file_info {
+    protected $user;
+
+    public function __construct($browser, $context) {
+        global $DB, $USER;
+
+        parent::__construct($browser, $context);
+
+        $userid = $context->instanceid;
+
+        if ($userid == $USER->id) {
+            $this->user = $USER;
+        } else {
+            // if context exists user record should exist too ;-)
+            $this->user = $DB->get_record('user', array('id'=>$userid));
+        }
+    }
+
+    public function get_params() {
+        return array('contextid'=>$this->context->id,
+                     'filearea' =>null,
+                     'itemid'   =>null,
+                     'filepath' =>null,
+                     'filename' =>null);
+    }
+
+    public function get_visible_name() {
+        return fullname($this->user, true);
+    }
+
+    public function is_directory() {
+        return true;
+    }
+
+    public function get_children() {
+        global $USER, $CFG;
+
+        // only current user for now
+        return array($this->browser->get_file_info(get_context_instance(CONTEXT_USER, $USER->id), 'user_private', 0));
+    }
+
+    public function get_parent() {
+        return $this->browser->get_file_info(get_context_instance(CONTEXT_SYSTEM));
+    }
+}
diff --git a/lib/file/file_packer.php b/lib/file/file_packer.php
new file mode 100644 (file)
index 0000000..2b826f2
--- /dev/null
@@ -0,0 +1,5 @@
+<?php  //$Id$
+
+class file_packer {
+
+}
\ No newline at end of file
diff --git a/lib/file/file_storage.php b/lib/file/file_storage.php
new file mode 100644 (file)
index 0000000..f4e95ab
--- /dev/null
@@ -0,0 +1,632 @@
+<?php  //$Id$
+
+require_once("$CFG->libdir/file/stored_file.php");
+
+class file_storage {
+    private $filedir;
+
+    /**
+     * Contructor
+     * @param string $filedir full path to pool directory
+     */
+    public function __construct() {
+        global $CFG;
+        if (isset($CFG->filedir)) {
+            $this->filedir = $CFG->filedir;
+        } else {
+            $this->filedir = $CFG->dataroot.'/filedir';
+        }
+
+        // make sure the file pool directory exists
+        if (!is_dir($this->filedir)) {
+            if (!check_dir_exists($this->filedir, true, true)) {
+                throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble
+            }
+            // place warning file in file pool root
+            $fp = fopen($this->filedir.'/warning.txt', 'w');
+            fwrite($fp, 'This directory contains the content of uploaded files and is controlled by Moodle code. Do not manually move, change or rename any of the files and subdirectories here.');
+            fclose($fp);
+            unset($fp);
+        }
+    }
+
+    /**
+     * Calculates sha1 hash of unique full path name information
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $filepath
+     * @param string $filename
+     * @return string
+     */
+    public static function get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename) {
+        return sha1($contextid.$filearea.$itemid.$filepath.$filename);
+    }
+
+    /**
+     * Does this file exist?
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $filepath
+     * @param string $filename
+     * @return bool
+     */
+    public function file_exists($contextid, $filearea, $itemid, $filepath, $filename) {
+        $filepath = clean_param($filepath, PARAM_PATH);
+        $filename = clean_param($filename, PARAM_FILE);
+
+        if ($filename === '') {
+            $filename = '.';
+        }
+
+        $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename);
+        return $this->file_exists_by_hash($pathnamehash);
+    }
+
+    /**
+     * Does this file exist?
+     * @param string $pathnamehash
+     * @return bool
+     */
+    public function file_exists_by_hash($pathnamehash) {
+        global $DB;
+
+        return $DB->record_exists('files', array('pathnamehash'=>$pathnamehash));
+    }
+
+    /**
+     * Fetch file using local file id
+     * @param int $fileid
+     * @return mixed stored_file instance if exists, false if not
+     */
+    public function get_file_by_id($fileid) {
+        global $DB;
+
+        if ($file_record = $DB->get_record('files', array('id'=>$fileid))) {
+            return new stored_file($this, $file_record);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Fetch file using local file full pathname hash
+     * @param string $pathnamehash
+     * @return mixed stored_file instance if exists, false if not
+     */
+    public function get_file_by_hash($pathnamehash) {
+        global $DB;
+
+        if ($file_record = $DB->get_record('files', array('pathnamehash'=>$pathnamehash))) {
+            return new stored_file($this, $file_record);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Fetch file
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $filepath
+     * @param string $filename
+     * @return mixed stored_file instance if exists, false if not
+     */
+    public function get_file($contextid, $filearea, $itemid, $filepath, $filename) {
+        global $DB;
+
+        $filepath = clean_param($filepath, PARAM_PATH);
+        $filename = clean_param($filename, PARAM_FILE);
+
+        if ($filename === '') {
+            $filename = '.';
+        }
+
+        $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, $filename);
+        return $this->get_file_by_hash($pathnamehash);
+    }
+
+    /**
+     * Returns all area files (optionally limited by itemid)
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid (all files if not specified)
+     * @param string $sort
+     * @param bool $includedirs
+     * @return array of stored_files
+     */
+    public function get_area_files($contextid, $filearea, $itemid=false, $sort="itemid, filepath, filename", $inludedirs=true) {
+        global $DB;
+
+        $conditions = array('contextid'=>$contextid, 'filearea'=>$filearea);
+        if ($itemid !== false) {
+            $conditions['itemid'] = $itemid;
+        }
+
+        $result = array();
+        $file_records = $DB->get_records('files', $conditions, $sort);
+        foreach ($file_records as $file_record) {
+            if (!$inludedirs and $file_record->filename === '.') {
+                continue;
+            }
+            $result[] = new stored_file($this, $file_record);
+        }
+        return $result;
+    }
+
+    /**
+     * Delete all area files (optionally limited by itemid)
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid (all files if not specified)
+     * @return success
+     */
+    public function delete_area_files($contextid, $filearea, $itemid=false) {
+        global $DB;
+
+        $conditions = array('contextid'=>$contextid, 'filearea'=>$filearea);
+        if ($itemid !== false) {
+            $conditions['itemid'] = $itemid;
+        }
+
+        $success = true;
+
+        $file_records = $DB->get_records('files', $conditions);
+        foreach ($file_records as $file_record) {
+            $stored_file = new stored_file($this, $file_record);
+            $success = $stored_file->delete() && $success;
+        }
+
+        return $success;
+    }
+
+    /**
+     * Recursively creates director
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $filepath
+     * @param string $filename
+     * @return bool success
+     */
+    public function create_directory($contextid, $filearea, $itemid, $filepath, $userid=null) {
+        global $DB;
+
+        // validate all parameters, we do not want any rubbish stored in database, right?
+        if (!is_number($contextid) or $contextid < 1) {
+            throw new file_exception('localfileproblem', 'Invalid contextid');
+        }
+
+        $filearea = clean_param($filearea, PARAM_SAFEDIR);
+        if ($filearea === '') {
+            throw new file_exception('localfileproblem', 'Invalid filearea');
+        }
+
+        if (!is_number($itemid) or $itemid < 0) {
+            throw new file_exception('localfileproblem', 'Invalid itemid');
+        }
+
+        $filepath = clean_param($filepath, PARAM_PATH);
+        if (strpos($filepath, '/') !== 0 or strrpos($filepath, '/') !== strlen($filepath)-1) {
+            // path must start and end with '/'
+            throw new file_exception('localfileproblem', 'Invalid file path');
+        }
+
+        $pathnamehash = $this->get_pathname_hash($contextid, $filearea, $itemid, $filepath, '.');
+
+        if ($dir_info = $this->get_file_by_hash($pathnamehash)) {
+            return $dir_info;
+        }
+
+        static $contenthash = null;
+        if (!$contenthash) {
+            $this->add_to_pool_string('');
+            $contenthash = sha1('');
+        }
+
+        $now = time();
+
+        $dir_record = new object();
+        $dir_record->contextid = $contextid;
+        $dir_record->filearea  = $filearea;
+        $dir_record->itemid    = $itemid;
+        $dir_record->filepath  = $filepath;
+        $dir_record->filename  = '.';
+        $dir_record->contenthash  = $contenthash;
+        $dir_record->filesize  = 0;
+
+        $dir_record->timecreated  = $now;
+        $dir_record->timemodified = $now;
+        $dir_record->mimetype     = null;
+        $dir_record->userid       = $userid;
+
+        $dir_record->pathnamehash = $pathnamehash;
+
+        $DB->insert_record('files', $dir_record);
+        $dir_info = $this->get_file_by_hash($pathnamehash);
+
+        if ($filepath !== '/') {
+            //recurse to parent dirs
+            $filepath = trim($filepath, '/');
+            $filepath = explode('/', $filepath);
+            array_pop($filepath);
+            $filepath = implode('/', $filepath);
+            $filepath = ($filepath === '') ? '/' : "/$filepath/";
+            $this->create_directory($contextid, $filearea, $itemid, $filepath, $userid);
+        }
+
+        return $dir_info;
+    }
+
+    /**
+     * Add new local file based on existing local file
+     * @param mixed $file_record object or array describing changes
+     * @param int $fid id of existing local file
+     * @return object stored_file instance
+     */
+    public function create_file_from_localfile($file_record, $fid) {
+        global $DB;
+
+        $file_record = (array)$file_record; // we support arrays too
+        unset($file_record['id']);
+        unset($file_record['filesize']);
+        unset($file_record['contenthash']);
+
+        $now = time();
+
+        if ($newrecord = $DB->get_record('files', array('id'=>$fid))) {
+            throw new file_exception('localfileproblem', 'File does not exist');
+        }
+
+        unset($newrecord->id);
+
+        foreach ($file_record as $key=>$value) {
+            // validate all parameters, we do not want any rubbish stored in database, right?
+            if ($key == 'contextid' and (!is_number($value) or $value < 1)) {
+                throw new file_exception('localfileproblem', 'Invalid contextid');
+            }
+
+            if ($key == 'filearea') {
+                $value = clean_param($value, PARAM_SAFEDIR);
+                if ($value === '') {
+                    throw new file_exception('localfileproblem', 'Invalid filearea');
+                }
+            }
+
+            if ($key == 'itemid' and (!is_number($value) or $value < 0)) {
+                throw new file_exception('localfileproblem', 'Invalid itemid');
+            }
+
+
+            if ($key == 'filepath') {
+                $value = clean_param($value, PARAM_PATH);
+                if (strpos($value, '/') !== 0 or strpos($value, '/') !== strlen($value)-1) {
+                    // path must start and end with '/'
+                    throw new file_exception('localfileproblem', 'Invalid file path');
+                }
+            }
+
+            if ($key == 'filename') {
+                $value = clean_param($value, PARAM_FILE);
+                if ($value === '') {
+                    // path must start and end with '/'
+                    throw new file_exception('localfileproblem', 'Invalid file name');
+                }
+            }
+
+            $newrecord->$key = $value;
+        }
+
+        $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename);
+
+        try {
+            $newrecord->id = $DB->insert_record('files', $newrecord);
+        } catch (database_exception $e) {
+            $newrecord->id = false;
+        }
+
+        if (!$newrecord->id) {
+            throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid,
+                                                    $newrecord->filepath, $newrecord->filename);
+        }
+
+        $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid);
+
+        return new stored_file($this, $newrecord);
+    }
+
+    /**
+     * Add new local file
+     * @param mixed $file_record object or array describing file
+     * @param string $path path to file or content of file
+     * @return object stored_file instance
+     */
+    public function create_file_from_pathname($file_record, $pathname) {
+        global $DB;
+
+        $file_record = (object)$file_record; // we support arrays too
+
+        // validate all parameters, we do not want any rubbish stored in database, right?
+        if (!is_number($file_record->contextid) or $file_record->contextid < 1) {
+            throw new file_exception('localfileproblem', 'Invalid contextid');
+        }
+
+        $file_record->filearea = clean_param($file_record->filearea, PARAM_SAFEDIR);
+        if ($file_record->filearea === '') {
+            throw new file_exception('localfileproblem', 'Invalid filearea');
+        }
+
+        if (!is_number($file_record->itemid) or $file_record->itemid < 0) {
+            throw new file_exception('localfileproblem', 'Invalid itemid');
+        }
+
+        $file_record->filepath = clean_param($file_record->filepath, PARAM_PATH);
+        if (strpos($file_record->filepath, '/') !== 0 or strrpos($file_record->filepath, '/') !== strlen($file_record->filepath)-1) {
+            // path must start and end with '/'
+            throw new file_exception('localfileproblem', 'Invalid file path');
+        }
+
+        $file_record->filename = clean_param($file_record->filename, PARAM_FILE);
+        if ($file_record->filename === '') {
+            // path must start and end with '/'
+            throw new file_exception('localfileproblem', 'Invalid file name');
+        }
+
+        $now = time();
+
+        $newrecord = new object();
+
+        $newrecord->contextid = $file_record->contextid;
+        $newrecord->filearea  = $file_record->filearea;
+        $newrecord->itemid    = $file_record->itemid;
+        $newrecord->filepath  = $file_record->filepath;
+        $newrecord->filename  = $file_record->filename;
+
+        $newrecord->timecreated  = empty($file_record->timecreated) ? $now : $file_record->timecreated;
+        $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified;
+        $newrecord->mimetype     = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype;
+        $newrecord->userid       = empty($file_record->userid) ? null : $file_record->userid;
+
+        list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_to_pool_pathname($pathname);
+
+        $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename);
+
+        try {
+            $newrecord->id = $DB->insert_record('files', $newrecord);
+        } catch (database_exception $e) {
+            $newrecord->id = false;
+        }
+
+        if (!$newrecord->id) {
+            if ($newfile) {
+                $this->mark_delete_candidate($newrecord->contenthash);
+            }
+            throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid,
+                                                    $newrecord->filepath, $newrecord->filename);
+        }
+
+        $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid);
+
+        return new stored_file($this, $newrecord);
+    }
+
+    /**
+     * Add new local file
+     * @param mixed $file_record object or array describing file
+     * @param string $content content of file
+     * @return object stored_file instance
+     */
+    public function create_file_from_string($file_record, $content) {
+        global $DB;
+
+        $file_record = (object)$file_record; // we support arrays too
+
+        // validate all parameters, we do not want any rubbish stored in database, right?
+        if (!is_number($file_record->contextid) or $file_record->contextid < 1) {
+            throw new file_exception('localfileproblem', 'Invalid contextid');
+        }
+
+        $file_record->filearea = clean_param($file_record->filearea, PARAM_SAFEDIR);
+        if ($file_record->filearea === '') {
+            throw new file_exception('localfileproblem', 'Invalid filearea');
+        }
+
+        if (!is_number($file_record->itemid) or $file_record->itemid < 0) {
+            throw new file_exception('localfileproblem', 'Invalid itemid');
+        }
+
+        $file_record->filepath = clean_param($file_record->filepath, PARAM_PATH);
+        if (strpos($file_record->filepath, '/') !== 0 or strrpos($file_record->filepath, '/') !== strlen($file_record->filepath)-1) {
+            // path must start and end with '/'
+            throw new file_exception('localfileproblem', 'Invalid file path');
+        }
+
+        $file_record->filename = clean_param($file_record->filename, PARAM_FILE);
+        if ($file_record->filename === '') {
+            // path must start and end with '/'
+            throw new file_exception('localfileproblem', 'Invalid file name');
+        }
+
+        $now = time();
+
+        $newrecord = new object();
+
+        $newrecord->contextid = $file_record->contextid;
+        $newrecord->filearea  = $file_record->filearea;
+        $newrecord->itemid    = $file_record->itemid;
+        $newrecord->filepath  = $file_record->filepath;
+        $newrecord->filename  = $file_record->filename;
+
+        $newrecord->timecreated  = empty($file_record->timecreated) ? $now : $file_record->timecreated;
+        $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified;
+        $newrecord->mimetype     = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype;
+        $newrecord->userid       = empty($file_record->userid) ? null : $file_record->userid;
+
+        list($newrecord->contenthash, $newrecord->filesize, $newfile) = $this->add_to_pool_string($content);
+
+        $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename);
+
+        try {
+            $newrecord->id = $DB->insert_record('files', $newrecord);
+        } catch (database_exception $e) {
+            $newrecord->id = false;
+        }
+
+        if (!$newrecord->id) {
+            if ($newfile) {
+                $this->mark_delete_candidate($newrecord->contenthash);
+            }
+            throw new stored_file_creation_exception($newrecord->contextid, $newrecord->filearea, $newrecord->itemid,
+                                                    $newrecord->filepath, $newrecord->filename);
+        }
+
+        $this->create_directory($newrecord->contextid, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid);
+
+        return new stored_file($this, $newrecord);
+    }
+
+    /**
+     * Add file content to sha1 pool
+     * @param string $pathname path to file
+     * @param string sha1 hash of content if known (performance only)
+     * @return array(contenthash, filesize, newfile)
+     */
+    public function add_to_pool_pathname($pathname, $contenthash=null) {
+        if (!is_readable($pathname)) {
+            throw new file_exception('localfilecannotread');
+        }
+
+        if (is_null($contenthash)) {
+            $contenthash = sha1_file($pathname);
+        }
+
+        $filesize = filesize($pathname);
+
+        $hashpath = $this->path_from_hash($contenthash);
+        $hashfile = "$hashpath/$contenthash";
+
+        if (file_exists($hashfile)) {
+            if (filesize($hashfile) !== $filesize) {
+                throw new file_pool_content_exception($contenthash);
+            }
+            $newfile = false;
+
+        } else {
+            if (!check_dir_exists($hashpath, true, true)) {
+                throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble
+            }
+            $newfile = true;
+
+            $fs = fopen($pathname, 'rb');
+            $fp = fopen($hashfile, 'wb');
+            while(!feof($fs)) {
+                $buf = fread($fs, 65536);
+                if ($buf === false) {
+                    throw new file_exception('localfilecannotread');
+                }
+                fwrite($fp, $buf);
+            }
+            fclose($fp);
+            fclose($fs);
+
+            if (filesize($hashfile) !== $filesize) {
+                @unlink($hashfile);
+                throw new file_pool_content_exception($contenthash);
+            }
+        }
+
+
+        return array($contenthash, $filesize, $newfile);
+    }
+
+    /**
+     * Add string content to sha1 pool
+     * @param string $content file content - binary string
+     * @return array(contenthash, filesize, newfile)
+     */
+    public function add_to_pool_string($content) {
+        $contenthash = sha1($content);
+        $filesize = strlen($content); // binary length
+
+        $hashpath = $this->path_from_hash($contenthash);
+        $hashfile = "$hashpath/$contenthash";
+
+
+        if (file_exists($hashfile)) {
+            if (filesize($hashfile) !== $filesize) {
+                throw new file_pool_content_exception($contenthash);
+            }
+            $newfile = false;
+
+        } else {
+            if (!check_dir_exists($hashpath, true, true)) {
+                throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble
+            }
+            $newfile = true;
+
+            $fp = fopen($hashfile, 'wb');
+
+            fwrite($fp, $content);
+            fclose($fp);
+
+            if (filesize($hashfile) !== $filesize) {
+                @unlink($hashfile);
+                throw new file_pool_content_exception($contenthash);
+            }
+        }
+
+        return array($contenthash, $filesize, $newfile);
+    }
+
+    /**
+     * Return path to file with given hash
+     *
+     * DO NOT USE - should be protected, but protected is dumb in PHP
+     *
+     * @param string $contenthash
+     * @return string expected file location
+     */
+    public function path_from_hash($contenthash) {
+        $l1 = $contenthash[0].$contenthash[1];
+        $l2 = $contenthash[2].$contenthash[3];
+        $l3 = $contenthash[4].$contenthash[5];
+        return "$this->filedir/$l1/$l2/$l3";
+    }
+
+    /**
+     * Marks pool file as candidate for deleting
+     * @param string $contenthash
+     */
+    public function mark_delete_candidate($contenthash) {
+        global $DB;
+
+        if ($DB->record_exists('files_cleanup', array('contenthash'=>$contenthash))) {
+            return;
+        }
+        $rec = new object();
+        $rec->contenthash = $contenthash;
+        $DB->insert_record('files_cleanup', $rec);
+    }
+
+    /**
+     * Cron cleanup job.
+     */
+    public function cron() {
+        global $DB;
+
+        //TODO: there is a small chance that reused files might be deleted
+        //      if this function takes too long we should add some table locking here
+
+        $sql = "SELECT 1 AS id, fc.contenthash
+                  FROM {files_cleanup} fc
+                  LEFT JOIN {files} f ON f.contenthash = fc.contenthash
+                 WHERE f.id IS NULL";
+        while ($hash = $DB->get_record_sql($sql, null, true)) {
+            $file = $this->path_from_hash($hash->contenthash).'/'.$hash->contenthash;
+            @unlink($file);
+            $DB->delete_records('files_cleanup', array('contenthash'=>$hash->contenthash));
+        }
+    }
+}
diff --git a/lib/file/stored_file.php b/lib/file/stored_file.php
new file mode 100644 (file)
index 0000000..375809c
--- /dev/null
@@ -0,0 +1,122 @@
+<?php  //$Id$
+
+/**
+ * Class representing local files stored in sha1 file pool
+ */
+class stored_file {
+    private $fs;
+    private $file_record;
+
+    /**
+     * Constructor
+     * @param object $fs file  storage instance
+     * @param object $file_record description of file
+     */
+    public function __construct($fs, $file_record) {
+        $this->fs = $fs;
+        $this->file_record = clone($file_record);
+    }
+
+    /**
+     * Is this a directory?
+     * @return bool
+     */
+    public function is_directory() {
+        return $this->file_record->filename === '.';
+    }
+
+    /**
+     * Delete file
+     * @return success
+     */
+    public function delete() {
+        global $DB;
+        $this->fs->mark_delete_candidate($this->file_record->contenthash);
+        return $DB->delete_records('files', array('id'=>$this->file_record->id));
+    }
+
+    /**
+     * Protected - devs must not gain direct access to this function
+     **/
+    protected function get_content_file_location() {
+        // NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
+        $hashpath = $this->fs->path_from_hash($this->file_record->contenthash);
+        return $hashpath.'/'.$this->file_record->contenthash;
+    }
+
+    /**
+     * Returns file handle - read only mode, no writing allowed into pool files!
+     * @return file handle
+     */
+    public function get_content_file_handle() {
+        $path = $this->get_content_file_location();
+        if (!is_readable($path)) {
+            throw new file_exception('localfilecannotread');
+        }
+        return fopen($path, 'rb'); //binary reading only!!
+    }
+
+    /**
+     * Dumps file content to page
+     * @return file handle
+     */
+    public function readfile() {
+        $path = $this->get_content_file_location();
+        if (!is_readable($path)) {
+            throw new file_exception('localfilecannotread');
+        }
+        readfile($path);
+    }
+
+    /**
+     * Returns file content as string
+     * @return string content
+     */
+    public function get_content() {
+        $path = $this->get_content_file_location();
+        if (!is_readable($path)) {
+            throw new file_exception('localfilecannotread');
+        }
+        return file_get_contents($this->get_content_file_location());
+    }
+
+    public function get_contextid() {
+        return $this->file_record->contextid;
+    }
+
+    public function get_filearea() {
+        return $this->file_record->filearea;
+    }
+
+    public function get_itemid() {
+        return $this->file_record->itemid;
+    }
+
+    public function get_filepath() {
+        return $this->file_record->filepath;
+    }
+
+    public function get_filename() {
+        return $this->file_record->filename;
+    }
+
+    public function get_userid() {
+        return $this->file_record->userid;
+    }
+
+    public function get_filesize() {
+        return $this->file_record->filesize;
+    }
+
+    public function get_mimetype() {
+        return $this->file_record->mimetype;
+    }
+
+    public function get_timecreated() {
+        return $this->file_record->timecreated;
+    }
+
+    public function get_timemodified() {
+        return $this->file_record->timemodified;
+    }
+}
index cca0464..8d64540 100644 (file)
@@ -2,10 +2,14 @@
 
 define('BYTESERVING_BOUNDARY', 's1k2o3d4a5k6s7'); //unique string constant
 
+require_once("$CFG->libdir/file/file_exceptions.php");
+require_once("$CFG->libdir/file/file_storage.php");
+require_once("$CFG->libdir/file/file_browser.php");
+
 function get_file_url($path, $options=null, $type='coursefile') {
     global $CFG;
 
-    $path = str_replace('//', '/', $path);  
+    $path = str_replace('//', '/', $path);
     $path = trim($path, '/'); // no leading and trailing slashes
 
     // type of file
@@ -309,7 +313,7 @@ function download_file_content($url, $headers=null, $postdata=null, $fullrespons
  *   Unknown types should use the 'xxx' entry which includes defaults.
  */
 function get_mimetypes_array() {
-    return array (
+    static $mimearray = array (
         'xxx'  => array ('type'=>'document/unknown', 'icon'=>'unknown.gif'),
         '3gp'  => array ('type'=>'video/quicktime', 'icon'=>'video.gif'),
         'ai'   => array ('type'=>'application/postscript', 'icon'=>'image.gif'),
@@ -464,6 +468,7 @@ function get_mimetypes_array() {
         'xsl'  => array ('type'=>'text/xml', 'icon'=>'xml.gif'),
         'zip'  => array ('type'=>'application/zip', 'icon'=>'zip.gif')
     );
+    return $mimearray;
 }
 
 /**
@@ -476,10 +481,7 @@ function get_mimetypes_array() {
  * @return string Requested piece of information from array
  */
 function mimeinfo($element, $filename) {
-    static $mimeinfo = null;
-    if (is_null($mimeinfo)) {
-        $mimeinfo = get_mimetypes_array();
-    }
+    $mimeinfo = get_mimetypes_array();
 
     if (eregi('\.([a-z0-9]+)$', $filename, $match)) {
         if (isset($mimeinfo[strtolower($match[1])][$element])) {
@@ -500,8 +502,7 @@ function mimeinfo($element, $filename) {
  * @return string Requested piece of information from array
  */
 function mimeinfo_from_type($element, $mimetype) {
-    static $mimeinfo;
-    $mimeinfo=get_mimetypes_array();
+    $mimeinfo = get_mimetypes_array();
 
     foreach($mimeinfo as $values) {
         if($values['type']==$mimetype) {
@@ -521,8 +522,7 @@ function mimeinfo_from_type($element, $mimetype) {
  * @return string Requested piece of information from array
  */
 function mimeinfo_from_icon($element, $icon) {
-    static $mimeinfo;
-    $mimeinfo=get_mimetypes_array();
+    $mimeinfo = get_mimetypes_array();
 
     if (preg_match("/\/(.*)/", $icon, $matches)) {
         $icon = $matches[1];
@@ -639,6 +639,8 @@ function send_temp_file_finished($path) {
 function send_file($path, $filename, $lifetime=86400 , $filter=0, $pathisstring=false, $forcedownload=false, $mimetype='') {
     global $CFG, $COURSE, $SESSION;
 
+    session_write_close(); // unlock session during fileserving
+
     // Use given MIME type if specified, otherwise guess it using mimeinfo.
     // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O
     // only Firefox saves all files locally before opening when content-disposition: attachment stated
@@ -727,7 +729,8 @@ function send_file($path, $filename, $lifetime=86400 , $filter=0, $pathisstring=
                     $ranges = false;
                 }
                 if ($ranges) {
-                    byteserving_send_file($path, $mimetype, $ranges);
+                    $handle = fopen($filename, 'rb');
+                    byteserving_send_file($handle, $mimetype, $ranges, $filesize);
                 }
             }
         } else {
@@ -815,6 +818,177 @@ function send_file($path, $filename, $lifetime=86400 , $filter=0, $pathisstring=
     die; //no more chars to output!!!
 }
 
+/**
+ * Handles the sending of file data to the user's browser, including support for
+ * byteranges etc.
+ * @param object $stored_file local file object
+ * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
+ * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
+ * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
+ * @param string $filename Override filename
+ * @param string $mimetype Include to specify the MIME type; leave blank to have it guess the type from $filename
+ */
+function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownload=false, $filename=null) {
+    global $CFG, $COURSE, $SESSION;
+
+    session_write_close(); // unlock session during fileserving
+
+    // Use given MIME type if specified, otherwise guess it using mimeinfo.
+    // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O
+    // only Firefox saves all files locally before opening when content-disposition: attachment stated
+    $filename     = is_null($filename) ? $stored_file->get_filename() : $filename;
+    $isFF         = check_browser_version('Firefox', '1.5'); // only FF > 1.5 properly tested
+    $mimetype     = ($forcedownload and !$isFF) ? 'application/x-forcedownload' :
+                         ($stored_file->get_mimetype() ? $stored_file->get_mimetype() : mimeinfo('type', $filename));
+    $lastmodified = $stored_file->get_timemodified();
+    $filesize     = $stored_file->get_filesize();
+
+    //IE compatibiltiy HACK!
+    if (ini_get('zlib.output_compression')) {
+        ini_set('zlib.output_compression', 'Off');
+    }
+
+    //try to disable automatic sid rewrite in cookieless mode
+    @ini_set("session.use_trans_sid", "false");
+
+    //do not put '@' before the next header to detect incorrect moodle configurations,
+    //error should be better than "weird" empty lines for admins/users
+    //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
+    header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
+
+    // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
+    if (check_browser_version('MSIE')) {
+        $filename = rawurlencode($filename);
+    }
+
+    if ($forcedownload) {
+        @header('Content-Disposition: attachment; filename="'.$filename.'"');
+    } else {
+        @header('Content-Disposition: inline; filename="'.$filename.'"');
+    }
+
+    if ($lifetime > 0) {
+        @header('Cache-Control: max-age='.$lifetime);
+        @header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
+        @header('Pragma: ');
+
+        if (empty($CFG->disablebyteserving) && $mimetype != 'text/plain' && $mimetype != 'text/html') {
+
+            @header('Accept-Ranges: bytes');
+
+            if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
+                // byteserving stuff - for acrobat reader and download accelerators
+                // see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
+                // inspired by: http://www.coneural.org/florian/papers/04_byteserving.php
+                $ranges = false;
+                if (preg_match_all('/(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) {
+                    foreach ($ranges as $key=>$value) {
+                        if ($ranges[$key][1] == '') {
+                            //suffix case
+                            $ranges[$key][1] = $filesize - $ranges[$key][2];
+                            $ranges[$key][2] = $filesize - 1;
+                        } else if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) {
+                            //fix range length
+                            $ranges[$key][2] = $filesize - 1;
+                        }
+                        if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) {
+                            //invalid byte-range ==> ignore header
+                            $ranges = false;
+                            break;
+                        }
+                        //prepare multipart header
+                        $ranges[$key][0] =  "\r\n--".BYTESERVING_BOUNDARY."\r\nContent-Type: $mimetype\r\n";
+                        $ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/$filesize\r\n\r\n";
+                    }
+                } else {
+                    $ranges = false;
+                }
+                if ($ranges) {
+                    byteserving_send_file($stored_file->get_content_file_handle(), $mimetype, $ranges, $filesize);
+                }
+            }
+        } else {
+            /// Do not byteserve (disabled, strings, text and html files).
+            @header('Accept-Ranges: none');
+        }
+    } else { // Do not cache files in proxies and browsers
+        if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
+            @header('Cache-Control: max-age=10');
+            @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+            @header('Pragma: ');
+        } else { //normal http - prevent caching at all cost
+            @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
+            @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
+            @header('Pragma: no-cache');
+        }
+        @header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
+    }
+
+    if (empty($filter)) {
+        $filtered = false;
+        if ($mimetype == 'text/html' && !empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
+            //cookieless mode - rewrite links
+            @header('Content-Type: text/html');
+            $text = $stored_file->get_content();
+            $text = $SESSION->sid_ob_rewrite($text);
+            $filesize = strlen($text);
+            $filtered = true;
+        } else if ($mimetype == 'text/plain') {
+            @header('Content-Type: Text/plain; charset=utf-8'); //add encoding
+        } else {
+            @header('Content-Type: '.$mimetype);
+        }
+        @header('Content-Length: '.$filesize);
+        while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+        if ($filtered) {
+            echo $text;
+        } else {
+            $stored_file->readfile();
+        }
+
+    } else {     // Try to put the file through filters
+        if ($mimetype == 'text/html') {
+            $options = new object();
+            $options->noclean = true;
+            $options->nocache = true; // temporary workaround for MDL-5136
+            $text = $stored_file->get_content();
+            $text = file_modify_html_header($text);
+            $output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
+            if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
+                //cookieless mode - rewrite links
+                $output = $SESSION->sid_ob_rewrite($output);
+            }
+
+            @header('Content-Length: '.strlen($output));
+            @header('Content-Type: text/html');
+            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            echo $output;
+        // only filter text if filter all files is selected
+        } else if (($mimetype == 'text/plain') and ($filter == 1)) {
+            $options = new object();
+            $options->newlines = false;
+            $options->noclean = true;
+            $text = $stored_file->get_content();
+            $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
+            if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
+                //cookieless mode - rewrite links
+                $output = $SESSION->sid_ob_rewrite($output);
+            }
+
+            @header('Content-Length: '.strlen($output));
+            @header('Content-Type: text/html; charset=utf-8'); //add encoding
+            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            echo $output;
+        } else {    // Just send it out raw
+            @header('Content-Length: '.$filesize);
+            @header('Content-Type: '.$mimetype);
+            while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
+            $stored_file->readfile();
+        }
+    }
+    die; //no more chars to output!!!
+}
+
 function get_records_csv($file, $table) {
     global $CFG, $DB;
 
@@ -984,9 +1158,8 @@ function readfile_chunked($filename, $retbytes=true) {
 /**
  * Send requested byterange of file.
  */
-function byteserving_send_file($filename, $mimetype, $ranges) {
+function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
     $chunksize = 1*(1024*1024); // 1MB chunks - must be less than 2MB!
-    $handle = fopen($filename, 'rb');
     if ($handle === false) {
         die;
     }
@@ -994,7 +1167,7 @@ function byteserving_send_file($filename, $mimetype, $ranges) {
         $length = $ranges[0][2] - $ranges[0][1] + 1;
         @header('HTTP/1.1 206 Partial content');
         @header('Content-Length: '.$length);
-        @header('Content-Range: bytes '.$ranges[0][1].'-'.$ranges[0][2].'/'.filesize($filename));
+        @header('Content-Range: bytes '.$ranges[0][1].'-'.$ranges[0][2].'/'.$filesize);
         @header('Content-Type: '.$mimetype);
         while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
         $buffer = '';
@@ -1356,7 +1529,7 @@ class curl {
      * Download multiple files in parallel
      * $c = new curl;
      * $c->download(array(
-     *              array('url'=>'http://localhost/', 'file'=>fopen('a', 'wb')), 
+     *              array('url'=>'http://localhost/', 'file'=>fopen('a', 'wb')),
      *              array('url'=>'http://localhost/20/', 'file'=>fopen('b', 'wb'))
      *              ));
      */
index bc00cf0..6d5c8ec 100644 (file)
@@ -26,7 +26,7 @@ require_once 'HTML/QuickForm.php';
 require_once 'HTML/QuickForm/DHTMLRulesTableless.php';
 require_once 'HTML/QuickForm/Renderer/Tableless.php';
 
-require_once $CFG->libdir.'/uploadlib.php';
+require_once $CFG->libdir.'/uploadlib.php'; // TODO: remove
 
 /**
  * Callback called when PEAR throws an error
@@ -52,30 +52,24 @@ if ($CFG->debug >= DEBUG_ALL){
  * You will write your own definition() method which performs the form set up.
  */
 class moodleform {
-    var $_formname;       // form name
+    protected $_formname;       // form name
     /**
      * quickform object definition
      *
      * @var MoodleQuickForm
      */
-    var $_form;
+    protected $_form;
     /**
      * globals workaround
      *
      * @var array
      */
-    var $_customdata;
-    /**
-     * file upload manager
-     *
-     * @var upload_manager
-     */
-    var $_upload_manager; //
+    protected $_customdata;
     /**
      * definition_after_data executed flag
      * @var definition_finalized
      */
-    var $_definition_finalized = false;
+    protected $_definition_finalized = false;
 
     /**
      * The constructor function calls the abstract function definition() and it will then
@@ -112,7 +106,6 @@ class moodleform {
         if (!$editable){
             $this->_form->hardFreeze();
         }
-        $this->set_upload_manager(new upload_manager());
 
         $this->definition();
 
@@ -197,6 +190,8 @@ class moodleform {
      * Internal method. Validates all uploaded files.
      */
     function _validate_files(&$files) {
+        global $CFG, $COURSE;
+
         $files = array();
 
         if (empty($_FILES)) {
@@ -204,33 +199,106 @@ class moodleform {
             // note: server side rules do not work for files - use custom verification in validate() instead
             return true;
         }
-        $errors = array();
-        $mform =& $this->_form;
 
-        // check the files
-        $status = $this->_upload_manager->preprocess_files();
+        $errors = array();
+        $filenames = array();
 
         // now check that we really want each file
         foreach ($_FILES as $elname=>$file) {
-            if ($mform->elementExists($elname) and $mform->getElementType($elname)=='file') {
-                $required = $mform->isElementRequired($elname);
-                if (!empty($this->_upload_manager->files[$elname]['uploadlog']) and empty($this->_upload_manager->files[$elname]['clear'])) {
-                    if (!$required and $file['error'] == UPLOAD_ERR_NO_FILE) {
-                        // file not uploaded and not required - ignore it
-                        continue;
-                    }
-                    $errors[$elname] = $this->_upload_manager->files[$elname]['uploadlog'];
+            $required = $this->_form->isElementRequired($elname);
 
-                } else if (!empty($this->_upload_manager->files[$elname]['clear'])) {
-                    $files[$elname] = $this->_upload_manager->files[$elname]['tmp_name'];
+            if ($file['error'] == 4 and $file['size'] == 0) {
+                if ($required) {
+                    $errors[$elname] = get_string('required');
                 }
-            } else {
-                print_error('cannotuploadfile');
+                unset($_FILES[$elname]);
+                continue;
+            }
+
+            if ($file['error'] > 0) {
+                switch ($file['error']) {
+                case 1: // UPLOAD_ERR_INI_SIZE
+                    $errmessage = get_string('uploadserverlimit');
+                    break;
+
+                case 2: // UPLOAD_ERR_FORM_SIZE
+                    $errmessage = get_string('uploadformlimit');
+                    break;
+
+                case 3: // UPLOAD_ERR_PARTIAL
+                    $errmessage = get_string('uploadpartialfile');
+                    break;
+
+                case 4: // UPLOAD_ERR_NO_FILE
+                    $errmessage = get_string('uploadnofilefound');
+                    break;
+
+                // Note: there is no error with a value of 5
+
+                case 6: // UPLOAD_ERR_NO_TMP_DIR
+                    $errmessage = get_string('uploadnotempdir');
+                    break;
+
+                case 7: // UPLOAD_ERR_CANT_WRITE
+                    $errmessage = get_string('uploadcantwrite');
+                    break;
+
+                case 8: // UPLOAD_ERR_EXTENSION
+                    $errmessage = get_string('uploadextension');
+                    break;
+
+                default:
+                    $errmessage = get_string('uploadproblem', $file['name']);
+                }
+                $errors[$elname] = $errmessage;
+                unset($_FILES[$elname]);
+                continue;
+            }
+
+            if (!is_uploaded_file($file['tmp_name'])) {
+                // TODO: improve error message
+                $errors[$elname] = get_string('error');
+                unset($_FILES[$elname]);
+                continue;
+            }
+
+            if (!$this->_form->elementExists($elname) or !$this->_form->getElementType($elname)=='file') {
+                // hmm, this file was not requested
+                unset($_FILES[$elname]);
+                continue;
+            }
+
+/*
+  // TODO: rethink the file scanning
+            if ($CFG->runclamonupload) {
+                if (!clam_scan_moodle_file($_FILES[$elname], $COURSE)) {
+                    $errors[$elname] = $_FILES[$elname]['uploadlog'];
+                    unset($_FILES[$elname]);
+                    continue;
+                }
+            }
+*/
+            $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
+            if ($filename === '') {
+                // TODO: improve error message - wrong chars
+                $errors[$elname] = get_string('error');
+                unset($_FILES[$elname]);
+                continue;
             }
+            if (in_array($filename, $filenames)) {
+                // TODO: improve error message - duplicate name
+                $errors[$elname] = get_string('error');
+                unset($_FILES[$elname]);
+                continue;
+            }
+            $filenames[] = $filename;
+            $_FILES[$elname]['name'] = $filename;
+
+            $files[$elname] = $_FILES[$elname]['tmp_name'];
         }
 
         // return errors if found
-        if ($status and 0 == count($errors)){
+        if (count($errors) == 0){
             return true;
 
         } else {
@@ -256,19 +324,8 @@ class moodleform {
         $this->_form->setDefaults($default_values);
     }
 
-    /**
-     * Set custom upload manager.
-     * Must be used BEFORE creating of file element!
-     *
-     * @param object $um - custom upload manager
-     */
     function set_upload_manager($um=false) {
-        if ($um === false) {
-            $um = new upload_manager();
-        }
-        $this->_upload_manager = $um;
-
-        $this->_form->setMaxFileSize($um->config->maxbytes);
+        debugging('Not used anymore, please fix code!');
     }
 
     /**
@@ -370,7 +427,7 @@ class moodleform {
     /**
      * Return submitted data if properly submitted or returns NULL if validation fails or
      * if there is no submitted data.
-     * 
+     *
      * note: $slashed param removed
      *
      * @return object submitted data; NULL if not valid or not submitted
@@ -417,60 +474,147 @@ class moodleform {
 
     /**
      * Save verified uploaded files into directory. Upload process can be customised from definition()
-     * method by creating instance of upload manager and storing it in $this->_upload_form
-     *
-     * @param string $destination where to store uploaded files
-     * @return bool success
+     * NOTE: please use save_stored_file() or save_file()
      */
     function save_files($destination) {
-        if ($this->is_submitted() and $this->is_validated()) {
-            return $this->_upload_manager->save_files($destination);
-        }
+        debugging('Not used anymore, please fix code! Use save_stored_file() or save_file() instead');
         return false;
     }
 
     /**
-     * If we're only handling one file (if inputname was given in the constructor)
-     * this will return the (possibly changed) filename of the file.
+     * Returns name of uploaded file.
+     * @param string $elname, first element if null
      * @return mixed false in case of failure, string if ok
      */
-    function get_new_filename() {
-        return $this->_upload_manager->get_new_filename();
+    function get_new_filename($elname=null) {
+        if (!$this->is_submitted() or !$this->is_validated()) {
+            return false;
+        }
+
+        if (is_null($elname)) {
+            if (empty($_FILES)) {
+                return false;
+            }
+            reset($_FILES);
+            $elname = key($_FILES);
+        }
+        if (!isset($_FILES[$elname])) {
+            return false;
+        }
+
+        return $_FILES[$elname]['name'];
     }
 
     /**
-     * Get content of uploaded file.
-     * @param $element name of file upload element
-     * @return mixed false in case of failure, string if ok
+     * Save file to standard filesystem
+     * @param string $elname name of element
+     * @param string $pathname full path name of file
+     * @param bool $override override file if exists
+     * @return bool success
      */
-    function get_file_content($elname) {
+    function save_file($elname, $pathname, $override=false) {
         if (!$this->is_submitted() or !$this->is_validated()) {
             return false;
         }
 
-        if (!$this->_form->elementExists($elname)) {
+        if (!isset($_FILES[$elname])) {
             return false;
         }
 
-        if (empty($this->_upload_manager->files[$elname]['clear'])) {
+        if (file_exists($pathname)) {
+            if ($override) {
+                if (!@unlink($pathname)) {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+        if (!$temp = @fopen($_FILES[$elname]['tmp_name'], "rb")) {
+            return false;
+        }
+        if (!$file = @fopen($pathname, "wb")) {
+            return false;
+        }
+
+        while (!feof($temp)) {
+            $data = fread($temp, 65536);
+            fwrite($file, $data);
+        }
+        fclose($file);
+        fclose($temp);
+
+        return true;
+    }
+
+    /**
+     * Save file to local filesystem pool
+     * @param string $elname name of element
+     * @param int $contextid
+     * @param string $filearea
+     * @param string $filepath
+     * @param string $filename - use specified filename, if not specified name of uploaded file used
+     * @param bool $override override file if exists
+     * @param int $userid
+     * @return mixed stored_file object or false if error; may throw exception if duplicate found
+     */
+    function save_stored_file($elname, $contextid, $filearea, $itemid, $filepath, $filename=null, $override=false, $userid=null) {
+        if (!$this->is_submitted() or !$this->is_validated()) {
             return false;
-        }        
+        }
 
-        if (empty($this->_upload_manager->files[$elname]['tmp_name'])) {
+        if (!isset($_FILES[$elname])) {
             return false;
         }
 
-        $data = "";
-        $file = @fopen($this->_upload_manager->files[$elname]['tmp_name'], "rb");
-        if ($file) {
-            while (!feof($file)) {
-                $data .= fread($file, 1024); // TODO: do we really have to do this?
+        $filename = is_null($filename) ? $_FILES[$elname]['name'] : $filename;
+
+        $fs = get_file_storage();
+
+        if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
+            if ($override) {
+                $file->delete();
+            } else {
+                return false;
             }
-            fclose($file);
-            return $data;
-        } else {
+        }
+
+        $file_record = new object();
+        $file_record->contextid = $contextid;
+        $file_record->filearea  = $filearea;
+        $file_record->itemid    = $itemid;
+        $file_record->filepath  = $filepath;
+        $file_record->filename  = $filename;
+        $file_record->userid    = $userid;
+
+        return $fs->create_file_from_pathname($file_record, $_FILES[$elname]['tmp_name']);
+    }
+
+    /**
+     * Get content of uploaded file.
+     * @param $element name of file upload element
+     * @return mixed false in case of failure, string if ok
+     */
+    function get_file_content($elname) {
+        if (!$this->is_submitted() or !$this->is_validated()) {
+            return false;
+        }
+
+        if (!isset($_FILES[$elname])) {
             return false;
         }
+
+        if (!$file = @fopen($_FILES[$elname]['tmp_name'], "rb")) {
+            return false;
+        }
+
+        $data = '';
+        while (!feof($file)) {
+            $data .= fread($file, 4048);
+        }
+        fclose($file);
+
+        return $data;
     }
 
     /**
@@ -625,7 +769,7 @@ class moodleform {
      * @param array  $attributes associative array of HTML attributes
      * @param int    $originalValue The original general state of the checkboxes before the user first clicks this element
      */
-    function add_checkbox_controller($groupid, $buttontext, $attributes, $originalValue = 0) { 
+    function add_checkbox_controller($groupid, $buttontext, $attributes, $originalValue = 0) {
         global $CFG;
         if (empty($text)) {
             $text = get_string('selectallornone', 'form');
@@ -642,7 +786,7 @@ class moodleform {
 
         $mform->addElement('hidden', "checkbox_controller$groupid");
         $mform->setConstants(array("checkbox_controller$groupid" => $new_select_value));
-        
+
         // Locate all checkboxes for this group and set their value, IF the optional param was given
         if (!is_null($select_value)) {
             foreach ($this->_form->_elements as $element) {
@@ -654,7 +798,7 @@ class moodleform {
 
         $checkbox_controller_name = 'nosubmit_checkbox_controller' . $groupid;
         $mform->registerNoSubmitButton($checkbox_controller_name);
-        
+
         // Prepare Javascript for submit element
         $js = "\n//<![CDATA[\n";
         if (!defined('HTML_QUICKFORM_CHECKBOXCONTROLLER_EXISTS')) {
@@ -664,29 +808,29 @@ function html_quickform_toggle_checkboxes(group) {
     var newvalue = false;
     var global = eval('html_quickform_checkboxgroup' + group + ';');
     if (global == 1) {
-        eval('html_quickform_checkboxgroup' + group + ' = 0;'); 
+        eval('html_quickform_checkboxgroup' + group + ' = 0;');
         newvalue = '';
     } else {
-        eval('html_quickform_checkboxgroup' + group + ' = 1;'); 
+        eval('html_quickform_checkboxgroup' + group + ' = 1;');
         newvalue = 'checked';
     }
 
     for (i = 0; i < checkboxes.length; i++) {
-        checkboxes[i].checked = newvalue; 
+        checkboxes[i].checked = newvalue;
     }
 }
 EOS;
             define('HTML_QUICKFORM_CHECKBOXCONTROLLER_EXISTS', true);
         }
         $js .= "\nvar html_quickform_checkboxgroup$groupid=$originalValue;\n";
-        
+
         $js .= "//]]>\n";
-        
+
         require_once("$CFG->libdir/form/submitlink.php");
         $submitlink = new MoodleQuickForm_submitlink($checkbox_controller_name, $attributes);
         $submitlink->_js = $js;
         $submitlink->_onclick = "html_quickform_toggle_checkboxes($groupid); return false;";
-        $mform->addElement($submitlink); 
+        $mform->addElement($submitlink);
         $mform->setDefault($checkbox_controller_name, $text);
     }
 
@@ -1702,7 +1846,7 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
     }
 
     function renderElement(&$element, $required, $error){
-        //manipulate id of all elements before rendering       
+        //manipulate id of all elements before rendering
         if (!is_null($element->getAttribute('id'))) {
             $id = $element->getAttribute('id');
         } else {
@@ -1715,7 +1859,7 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
         }
 
         //adding stuff to place holders in template
-        //check if this is a group element first 
+        //check if this is a group element first
         if (($this->_inGroup) and !empty($this->_groupElementTemplate)) {
                // so it gets substitutions for *each* element
             $html = $this->_groupTemplates[$element->getName()];       
@@ -1753,8 +1897,8 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
         }
         elseif (!isset($this->_templates[$element->getName()])) {
             $this->_templates[$element->getName()] = $html;
-        }  
-        
+        }
+
         parent::renderElement($element, $required, $error);
     }
 
@@ -1814,7 +1958,7 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
 document.write("'.addslashes_js($button_js).'")
 //]]>
 </script><noscript><div style="display:inline">'.$button_nojs.'</div></noscript>';  // the extra div should fix xhtml validation
-            
+
             $header_html = str_replace('{button}', $button, $header_html);
         } else {
             $header_html = str_replace('{button}', '', $header_html);
index 24c95af..7bb5b8b 100644 (file)
@@ -128,26 +128,25 @@ function create_profile_image_destination($id, $dir='user') {
  * it and saves it in the right place to be a "user" or "group" image.
  *
  * @param int $id user or group id
- * @param object $uploadmanager object referencing the image
+ * @param object $userform with imagefile upload field
  * @param string $dir type of entity - groups, user, ...
  * @return boolean success
  */
-function save_profile_image($id, $uploadmanager, $dir='user') {
-
-    if (!$uploadmanager) {
-        return false;
-    }
+function save_profile_image($id, $userform, $dir='user') {
 
     $destination = create_profile_image_destination($id, $dir);
     if ($destination === false) {
         return false;
     }
 
-    if (!$uploadmanager->save_files($destination)) {
+    $filename = $userform->get_new_filename('imagefile');
+    $pathname = $destination.'/'.$filename;
+
+    if (!$userform->save_file('imagefile', $pathname, true)) {
         return false;
     }
 
-    return process_profile_image($uploadmanager->get_new_filepath(), $destination);
+    return process_profile_image($pathname, $destination);
 }
 
 /**
index 1bb2c9a..ea3a299 100644 (file)
@@ -4471,6 +4471,45 @@ function email_welcome_message_to_user($course, $user=NULL) {
 
 /// FILE HANDLING  /////////////////////////////////////////////
 
+/**
+ * Returns local file storage instance
+ * @return object file_storage
+ */
+function get_file_storage() {
+    global $CFG;
+
+    static $fs = null;
+
+    if ($fs) {
+        return $fs;
+    }
+
+    require_once("$CFG->libdir/filelib.php");
+
+    $fs = new file_storage();
+
+    return $fs;
+}
+
+/**
+ * Returns local file storage instance
+ * @return object file_storage
+ */
+function get_file_browser() {
+    global $CFG;
+
+    static $fb = null;
+
+    if ($fb) {
+        return $fb;
+    }
+
+    require_once("$CFG->libdir/filelib.php");
+
+    $fb = new file_browser();
+
+    return $fb;
+}
 
 /**
  * Makes an upload directory for a particular module.
@@ -7776,35 +7815,11 @@ function check_dir_exists($dir, $create=false, $recursive=false) {
 
     $status = true;
 
-    if(!is_dir($dir)) {
+    if (!is_dir($dir)) {
         if (!$create) {
             $status = false;
         } else {
-            umask(0000);
-            if ($recursive) {
-            /// PHP 5.0 has recursive mkdir parameter, but 4.x does not :-(
-                $dir = str_replace('\\', '/', $dir); //windows compatibility
-            /// We are going to make it recursive under $CFG->dataroot only
-            /// (will help sites running open_basedir security and others)
-                $dir = str_replace($CFG->dataroot . '/', '', $dir);
-                $dirs = explode('/', $dir); /// Extract path parts
-            /// Iterate over each part with start point $CFG->dataroot
-                $dir = $CFG->dataroot . '/';
-                foreach ($dirs as $part) {
-                    if ($part == '') {
-                        continue;
-                    }
-                    $dir .= $part.'/';
-                    if (!is_dir($dir)) {
-                        if (!mkdir($dir, $CFG->directorypermissions)) {
-                            $status = false;
-                            break;
-                        }
-                    }
-                }
-            } else {
-                $status = mkdir($dir, $CFG->directorypermissions);
-            }
+            $status = mkdir($dir, $CFG->directorypermissions, $recursive);
         }
     }
     return $status;
index 751e269..d4f74fa 100644 (file)
@@ -7142,7 +7142,7 @@ class progress_bar {
     private $lastcall;
     private $time_start;
     private $minimum_time = 2; //min time between updates.
-    function __construct($html_id = 'pid', $width = 100, $autostart = false){
+    function __construct($html_id = 'pid', $width = 500, $autostart = false){
         $this->html_id  = $html_id;
         $this->clr      = new stdClass;
         $this->clr->done    = 'green';
index 6e99825..914b399 100644 (file)
@@ -35,7 +35,125 @@ function xmldb_assignment_upgrade($oldversion=0) {
         $DB->set_debug(true);
         upgrade_mod_savepoint($result, 2007101511, 'assignment');
     }
-    
+
+    if ($result && $oldversion < 2008073000) {
+
+        /////////////////////////////////////
+        /// new file storage upgrade code ///
+        /////////////////////////////////////
+
+        $fs = get_file_storage();
+
+        $sql = "SELECT s.id, s.userid, s.teacher, s.assignment, a.course
+                  FROM {assignment_submissions} s
+                  JOIN {assignment} a ON a.id = s.assignment
+              ORDER BY a.course, s.assignment";
+
+        $count = $DB->count_records_sql($sql); 
+
+        $lastcourse     = 0;
+        $lastassignment = 0;
+
+        if ($rs = $DB->get_recordset_sql($sql)) {
+
+            $pbar = new progress_bar('migrateassignmentfiles', 500, true);
+
+            $olddebug = $DB->get_debug();
+//            $DB->set_debug(false); // lower debug level, there might be many files
+            $i = 0;
+            foreach ($rs as $submission) {
+                $i++;
+                $basepath = "$CFG->dataroot/$submission->course/$CFG->moddata/assignment/$submission->assignment/$submission->userid/";
+                if (!file_exists($basepath)) {
+                    //no files
+                    continue;
+                }
+                $context = get_context_instance(CONTEXT_MODULE, $submission->assignment);
+
+                // migrate submitted files fisrt
+                $path = $basepath;
+                $filearea = 'assignment_submission';
+                $items = new DirectoryIterator($path);
+                foreach ($items as $item) {
+                    if (!$item->isFile()) {
+                        continue;
+                    }
+                    if (!$item->isReadable()) {
+                        notify(" File not readable, skipping: ".$path.$item->getFilename());
+                        continue;
+                    }
+                    $filename = clean_param($item->getFilename(), PARAM_FILE);
+                    if ($filename === '') {
+                        continue;
+                    }
+                    if (!$fs->file_exists($context->id, $filearea, '0', '/', $filename)) {
+                        $file_record = array('contextid'=>$context->id, 'filearea'=>$filearea, 'itemid'=>$submission->userid, 'filepath'=>'/', 'filename'=>$filename, 'userid'=>$submission->userid);
+                        if ($fs->create_file_from_pathname($file_record, $path.$item->getFilename())) {
+                            unlink($path.$item->getFilename());
+                        }
+                    }
+                }
+                unset($items); //release file handles
+
+                // migrate teacher response files
+                $path = $basepath.'responses/';
+                if (file_exists($path)) {
+                    $filearea = 'assignment_response';
+                    $items = new DirectoryIterator($path);
+                    foreach ($items as $item) {
+                        if (!$item->isFile()) {
+                            continue;
+                        }
+                        $filename = clean_param($item->getFilename(), PARAM_FILE);
+                        if ($filename === '') {
+                            continue;
+                        }
+                        if (!$fs->file_exists($context->id, $filearea, '0', '/', $filename)) {
+                            $file_record = array('contextid'=>$context->id, 'filearea'=>$filearea, 'itemid'=>$submission->userid, 'filepath'=>'/', 'filename'=>$filename,
+                                                 'timecreated'=>$item->getCTime(), 'timemodified'=>$item->getMTime());
+                            if ($submission->teacher) {
+                                $file_record['userid'] = $submission->teacher;
+                            }
+                            if ($fs->create_file_from_pathname($file_record, $path.$item->getFilename())) {
+                                unlink($path.$item->getFilename());
+                            }
+                        }
+                    }
+                    unset($items); //release file handles
+                    @rmdir("$CFG->dataroot/$submission->course/$CFG->moddata/assignment/$submission->assignment/$submission->userid/responses/");
+                }
+
+                @rmdir("$CFG->dataroot/$submission->course/$CFG->moddata/assignment/$submission->assignment/$submission->userid/");
+
+                if ($lastassignment and $lastassignment != $submission->assignment) {
+                    @rmdir("$CFG->dataroot/$lastcourse/$CFG->moddata/assignment/$lastassignment");
+                }
+
+                if ($lastcourse and $lastcourse != $submission->course) {
+                    @rmdir("$CFG->dataroot/$lastcourse/$CFG->moddata/assignment");
+                    @rmdir("$CFG->dataroot/$lastcourse/$CFG->moddata");
+                    @rmdir("$CFG->dataroot/$lastcourse");
+                }
+                $lastsubmission = $submission->assignment;
+                $lastcourse     = $submission->course;
+
+                $pbar->update($i, $count, "Migrated assignment submissions - $i/$count.");
+            }
+            $DB->set_debug($olddebug); // reset debug level
+            $rs->close();
+
+            // cleanup after the last submission
+            if ($lastcourse) {
+                @rmdir("$CFG->dataroot/$lastcourse/$CFG->moddata/assignment/$lastassignment");
+                @rmdir("$CFG->dataroot/$lastcourse/$CFG->moddata/assignment");
+                @rmdir("$CFG->dataroot/$lastcourse/$CFG->moddata");
+                @rmdir("$CFG->dataroot/$lastcourse");
+            }
+        }
+
+        upgrade_mod_savepoint($result, 2008073000, 'assignment');
+    }
+
     return $result;
 }
 
index 30cdb12..99f1365 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 require_once($CFG->libdir.'/eventslib.php');
+require_once($CFG->libdir.'/formslib.php');
 
 DEFINE ('ASSIGNMENT_COUNT_WORDS', 1);
 DEFINE ('ASSIGNMENT_COUNT_LETTERS', 2);
@@ -1600,6 +1601,11 @@ class assignment_base {
         }
     }
 
+    function send_file($filearea, $args) {
+        debugging('plugin does not implement file sending', DEBUG_DEVELOPER);
+        return false;
+    }
+
     /**
      * Returns a list of teachers that should be grading given submission
      */
@@ -1692,34 +1698,34 @@ class assignment_base {
             $userid = $USER->id;
         }
 
-        $filearea = $this->file_area_name($userid);
-
         $output = '';
 
-        if ($basedir = $this->file_area($userid)) {
-            if ($files = get_directory_list($basedir)) {
-                require_once($CFG->libdir.'/filelib.php');
-                $p = array(
-                    'userid' => $userid,
-                    'assignmentid' => $this->cm->id,
-                );
-                foreach ($files as $key => $file) {
-
-                    $icon = mimeinfo('icon', $file);
-                    $ffurl = get_file_url("$filearea/$file", array('forcedownload'=>1));
-
-                    $output .= '<img src="'.$CFG->pixpath.'/f/'.$icon.'" class="icon" alt="'.$icon.'" />'.
-                            '<a href="'.$ffurl.'" >'.$file.'</a>';
-                    if ($this->portfolio_exportable() && true) { // @todo replace with capability check
-                        $p['file'] = $file;
-                        $output .= portfolio_add_button('assignment_portfolio_caller', $p, null, false, true);
-                    }
-                    $output .= '<br />';
-                }
-                if ($this->portfolio_exportable() && true) { //@todo replace with check capability
-                    unset($p['file']);// for all files
-                    $output .= '<br />' . portfolio_add_button('assignment_portfolio_caller', $p, null, true, true);
+        $fs = get_file_storage();
+        $browser = get_file_browser();
+
+        $found = false;
+
+        if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $userid, "timemodified", false)) {
+            $p = array(
+                'userid' => $userid,
+                'assignmentid' => $this->cm->id,
+            );
+            foreach ($files as $file) {
+                $filename = $file->get_filename();
+                $found = true;
+                $mimetype = $file->get_mimetype();
+                $icon = mimeinfo_from_type('icon', $mimetype);
+                $path = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename);
+                $output .= '<a href="'.$path.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" class="icon" alt="'.$icon.'" />'.s($filename).'</a>';
+                if ($this->portfolio_exportable() && true) { // @todo replace with capability check
+                    $p['file'] = $file;
+                    $output .= portfolio_add_button('assignment_portfolio_caller', $p, null, false, true);
                 }
+                $output .= '<br />';
+            }
+            if ($this->portfolio_exportable() && true) { //@todo replace with check capability
+                unset($p['file']);// for all files
+                $output .= '<br />' . portfolio_add_button('assignment_portfolio_caller', $p, null, true, true);
             }
         }
 
@@ -1738,38 +1744,9 @@ class assignment_base {
      * @return int
      */
     function count_user_files($userid) {
-        global $CFG;
-
-        $filearea = $this->file_area_name($userid);
-
-        if ( is_dir($CFG->dataroot.'/'.$filearea) && $basedir = $this->file_area($userid)) {
-            if ($files = get_directory_list($basedir)) {
-                return count($files);
-            }
-        }
-        return 0;
-    }
-
-    /**
-     * Creates a directory file name, suitable for make_upload_directory()
-     *
-     * @param $userid int The user id
-     * @return string path to file area
-     */
-    function file_area_name($userid) {
-        global $CFG;
-
-        return $this->course->id.'/'.$CFG->moddata.'/assignment/'.$this->assignment->id.'/'.$userid;
-    }
-
-    /**
-     * Makes an upload directory
-     *
-     * @param $userid int The user id
-     * @return string path to file area.
-     */
-    function file_area($userid) {
-        return make_upload_directory( $this->file_area_name($userid) );
+        $fs = get_file_storage();
+        $files = $fs->get_area_files($this->context->id, 'assignment_submission', $userid, "id", false);
+        return count($files);
     }
 
     /**
@@ -1829,12 +1806,14 @@ class assignment_base {
      */
     function user_complete($user) {
         if ($submission = $this->get_submission($user->id)) {
-            if ($basedir = $this->file_area($user->id)) {
-                if ($files = get_directory_list($basedir)) {
-                    $countfiles = count($files)." ".get_string("uploadedfiles", "assignment");
-                    foreach ($files as $file) {
-                        $countfiles .= "; $file";
-                    }
+
+            $fs = get_file_storage();
+            $browser = get_file_browser();
+    
+            if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $user->id, "timemodified", false)) {
+                $countfiles = count($files)." ".get_string("uploadedfiles", "assignment");
+                foreach ($files as $file) {
+                    $countfiles .= "; ".$file->get_filename();
                 }
             }
 
@@ -1962,6 +1941,27 @@ class assignment_base {
     }
 } ////// End of the assignment_base class
 
+class mod_assignment_upload_file_form extends moodleform {
+    function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+
+        //TODO: improve upload size checking
+        $mform->setMaxFileSize($instance->assignment->maxbytes);
+
+        // visible elements
+        $mform->addElement('file', 'newfile', get_string('uploadafile'));
+
+        // hidden params
+        $mform->addElement('hidden', 'id', $instance->cm->id);
+        $mform->setType('id', PARAM_INT);
+        $mform->addElement('hidden', 'action', 'uploadfile');
+        $mform->setType('action', PARAM_ALPHA);
+
+        // buttons
+        $this->add_action_buttons(false, get_string('uploadthisfile'));
+    }
+}
 
 
 /// OTHER STANDARD FUNCTIONS ////////////////////////////////////////////////////////
@@ -2347,6 +2347,22 @@ function assignment_get_participants($assignmentid) {
     return ($students);
 }
 
+function assignment_pluginfile($course, $cminfo, $context, $filearea, $args) {
+    global $CFG, $DB;
+
+    if (!$assignment = $DB->get_record('assignment', array('id'=>$cminfo->instance))) {
+        return false;
+    }
+    if (!$cm = get_coursemodule_from_instance('assignment', $assignment->id, $course->id)) {
+        return false;
+    }
+
+    require_once($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php');
+    $assignmentclass = 'assignment_'.$assignment->assignmenttype;
+    $assignmentinstance = new $assignmentclass($cm->id, $assignment, $cm, $course);
+
+    return $assignmentinstance->send_file($filearea, $args);
+}
 /**
  * Checks if a scale is being used by an assignment
  *
@@ -3143,6 +3159,7 @@ class assignment_portfolio_caller extends portfolio_module_caller_base {
         if (is_callable(array($this->assignment, 'portfolio_prepare_package'))) {
             return $this->assignment->portfolio_prepare_package($tempdir);
         }
+error('TODO: covert');
         // default...
         $filearea = $CFG->dataroot . '/' . $this->assignment->file_area_name($this->userid);
         //@todo  penny this is a dreadful thing to have to call (replace with files api anyway)
@@ -3158,6 +3175,8 @@ class assignment_portfolio_caller extends portfolio_module_caller_base {
         if (is_callable(array($this->assignment, 'portfolio_get_sha1'))) {
             return $this->assignment->portfolio_get_sha1();
         }
+
+error('TODO: covert');
         // default ...
         $filearea = $CFG->dataroot . '/' . $this->assignment->file_area_name($this->userid);
         if ($this->file) {
index 1d68566..a2d0b6f 100644 (file)
@@ -1,5 +1,4 @@
 <?php // $Id$
-require_once($CFG->libdir.'/formslib.php');
 require_once($CFG->libdir . '/portfoliolib.php');
 require_once($CFG->dirroot . '/mod/assignment/lib.php');
 
@@ -157,29 +156,14 @@ class assignment_upload extends assignment_base {
 
         $submission = $this->get_submission($USER->id);
 
-        $struploadafile = get_string('uploadafile');
-        $maxbytes = $this->assignment->maxbytes == 0 ? $this->course->maxbytes : $this->assignment->maxbytes;
-        $strmaxsize = get_string('maxsize', '', display_size($maxbytes));
-
         if ($this->is_finalized($submission)) {
             // no uploading
             return;
         }
 
         if ($this->can_upload_file($submission)) {
-            echo '<div style="text-align:center">';
-            echo '<form enctype="multipart/form-data" method="post" action="upload.php">';
-            echo '<fieldset class="invisiblefieldset">';
-            echo "<p>$struploadafile ($strmaxsize)</p>";
-            echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
-            echo '<input type="hidden" name="action" value="uploadfile" />';
-            require_once($CFG->libdir.'/uploadlib.php');
-            upload_print_form_fragment(1,array('newfile'),null,false,null,0,$this->assignment->maxbytes,false);
-            echo '<input type="submit" name="save" value="'.get_string('uploadthisfile').'" />';
-            echo '</fieldset>';
-            echo '</form>';
-            echo '</div>';
-            echo '<br />';
+            $mform = new mod_assignment_upload_file_form('upload.php', $this);
+            $mform->display();
         }
 
     }
@@ -249,21 +233,15 @@ class assignment_upload extends assignment_base {
         $offset       = optional_param('offset', 0, PARAM_INT);
         $forcerefresh = optional_param('forcerefresh', 0, PARAM_BOOL);
 
+        $mform = new mod_assignment_upload_response_form("$CFG->wwwroot/mod/assignment/upload.php", $this);
+
+        $mform->set_data(array('id'=>$this->cm->id, 'offset'=>$offset, 'forcerefresh'=>$forcerefresh, 'userid'=>$submission->userid, 'mode'=>$mode));
+
         $output = get_string('responsefiles', 'assignment').': ';
 
-        $output .= '<form enctype="multipart/form-data" method="post" '.
-             "action=\"$CFG->wwwroot/mod/assignment/upload.php\">";
-        $output .= '<div>';
-        $output .= '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
-        $output .= '<input type="hidden" name="action" value="uploadresponse" />';
-        $output .= '<input type="hidden" name="mode" value="'.$mode.'" />';
-        $output .= '<input type="hidden" name="offset" value="'.$offset.'" />';
-        $output .= '<input type="hidden" name="userid" value="'.$submission->userid.'" />';
-        require_once($CFG->libdir.'/uploadlib.php');
-        $output .= upload_print_form_fragment(1,array('newfile'),null,false,null,0,0,true);
-        $output .= '<input type="submit" name="save" value="'.get_string('uploadthisfile').'" />';
-        $output .= '</div>';
-        $output .= '</form>';
+        ob_start();
+        $mform->display();
+        $output = ob_get_clean();
 
         if ($forcerefresh) {
             $output .= $this->update_main_listing($submission);
@@ -285,30 +263,35 @@ class assignment_upload extends assignment_base {
     function print_student_answer($userid, $return=false){
         global $CFG;
 
-        $filearea = $this->file_area_name($userid);
         $submission = $this->get_submission($userid);
 
         $output = '';
 
-        if ($basedir = $this->file_area($userid)) {
-            if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission)) {
-                $output .= '<strong>'.get_string('draft', 'assignment').':</strong> ';
-            }
+        if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission)) {
+            $output .= '<strong>'.get_string('draft', 'assignment').':</strong> ';
+        }
 
-            if ($this->notes_allowed() and !empty($submission->data1)) {
-                $output .= link_to_popup_window ('/mod/assignment/type/upload/notes.php?id='.$this->cm->id.'&amp;userid='.$userid,
-                                                'notes'.$userid, get_string('notes', 'assignment'), 500, 780, get_string('notes', 'assignment'), 'none', true, 'notesbutton'.$userid);
-                $output .= '&nbsp;';
-            }
+        if ($this->notes_allowed() and !empty($submission->data1)) {
+            $output .= link_to_popup_window ('/mod/assignment/type/upload/notes.php?id='.$this->cm->id.'&amp;userid='.$userid,
+                                            'notes'.$userid, get_string('notes', 'assignment'), 500, 780, get_string('notes', 'assignment'), 'none', true, 'notesbutton'.$userid);
+            $output .= '&nbsp;';
+        }
 
-            if ($files = get_directory_list($basedir, 'responses')) {
-                require_once($CFG->libdir.'/filelib.php');
-                foreach ($files as $key => $file) {
-                    $icon = mimeinfo('icon', $file);
-                    $ffurl = get_file_url("$filearea/$file");
-                    $output .= '<a href="'.$ffurl.'" ><img class="icon" src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.$file.'</a>&nbsp;';
-                }
+        $fs = get_file_storage();
+        $browser = get_file_browser();
+
+        if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $userid, "timemodified", false)) {
+
+            foreach ($files as $file) {
+                $filename = $file->get_filename();
+                $found = true;
+                $mimetype = $file->get_mimetype();
+                $icon = mimeinfo_from_type('icon', $mimetype);
+                $path = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename);
+                $output .= '<a href="'.$path.'" ><img class="icon" src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.s($filename).'</a>&nbsp;';
+    
             }
+
         }
         $output = '<div class="files">'.$output.'</div>';
         $output .= '<br />';
@@ -337,8 +320,6 @@ class assignment_upload extends assignment_base {
             $userid = $USER->id;
         }
 
-        $filearea = $this->file_area_name($userid);
-
         $output = '';
 
         $submission = $this->get_submission($userid);
@@ -357,36 +338,37 @@ class assignment_upload extends assignment_base {
 
         }
 
-        if ($basedir = $this->file_area($userid)) {
-            if ($files = get_directory_list($basedir, 'responses')) {
-                require_once($CFG->libdir.'/filelib.php');
-                $p = array(
-                    'userid' => $userid,
-                    'assignmentid' => $this->cm->id,
-                );
-                foreach ($files as $key => $file) {
-
-                    $icon = mimeinfo('icon', $file);
-                    $ffurl = get_file_url("$filearea/$file");
-
-                    $output .= '<a href="'.$ffurl.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" class="icon" alt="'.$icon.'" />'.$file.'</a>';
-
-                    if ($candelete) {
-                        $delurl  = "$CFG->wwwroot/mod/assignment/delete.php?id={$this->cm->id}&amp;file=$file&amp;userid={$submission->userid}&amp;mode=$mode&amp;offset=$offset";
-
-                        $output .= '<a href="'.$delurl.'">&nbsp;'
-                                  .'<img title="'.$strdelete.'" src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt="" /></a> ';
-                    }
-                    if (true) { // @todo penny replace with capability check
-                        $p['file'] = $file;
-                        $output .= portfolio_add_button('assignment_portfolio_caller', $p, '/mod/assignment/lib.php', false, true);
-                    }
-                    $output .= '<br />';
+        $fs = get_file_storage();
+        $browser = get_file_browser();
+
+        if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $userid, "timemodified", false)) {
+            $p = array(
+                'userid' => $userid,
+                'assignmentid' => $this->cm->id,
+            );
+            foreach ($files as $file) {
+                $filename = $file->get_filename();
+                $mimetype = $file->get_mimetype();
+                $icon = mimeinfo_from_type('icon', $mimetype);
+                $path = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename);
+                $output .= '<a href="'.$path.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" class="icon" alt="'.$icon.'" />'.s($filename).'</a>';
+    
+                if ($candelete) {
+                    $delurl  = "$CFG->wwwroot/mod/assignment/delete.php?id={$this->cm->id}&amp;file=".rawurlencode($filename)."&amp;userid={$submission->userid}&amp;mode=$mode&amp;offset=$offset";
+    
+                    $output .= '<a href="'.$delurl.'">&nbsp;'
+                              .'<img title="'.$strdelete.'" src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt="" /></a> ';
                 }
-                if (true) { //@todo penny replace with check capability
-                    unset($p['file']);// for all files
-                    $output .= '<br />' . portfolio_add_button('assignment_portfolio_caller', $p, '/mod/assignment/lib.php', true, true);
+    
+                if (true) { // @todo penny replace with capability check
+                    $p['file'] = $filename;
+                    $output .= portfolio_add_button('assignment_portfolio_caller', $p, '/mod/assignment/lib.php', false, true);
                 }
+                $output .= '<br />';
+            }
+            if (true) { //@todo penny replace with check capability
+                unset($p['file']);// for all files
+                $output .= '<br />' . portfolio_add_button('assignment_portfolio_caller', $p, '/mod/assignment/lib.php', true, true);
             }
         }
 
@@ -414,40 +396,35 @@ class assignment_upload extends assignment_base {
         $mode    = optional_param('mode', '', PARAM_ALPHA);
         $offset  = optional_param('offset', 0, PARAM_INT);
 
-        $filearea = $this->file_area_name($userid).'/responses';
-
         $output = '';
 
         $candelete = $this->can_manage_responsefiles();
         $strdelete   = get_string('delete');
 
-        if ($basedir = $this->file_area($userid)) {
-            $basedir .= '/responses';
+        $fs = get_file_storage();
+        $browser = get_file_browser();
 
-            if ($files = get_directory_list($basedir)) {
-                require_once($CFG->libdir.'/filelib.php');
-                foreach ($files as $key => $file) {
+        if ($files = $fs->get_area_files($this->context->id, 'assignment_response', $userid, "timemodified", false)) {
+            foreach ($files as $file) {
+                $filename = $file->get_filename();
+                $found = true;
+                $mimetype = $file->get_mimetype();
+                $icon = mimeinfo_from_type('icon', $mimetype);
+                $path = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_response/'.$userid.'/'.$filename);
 
-                    $icon = mimeinfo('icon', $file);
+                $output .= '<a href="'.$path.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.$filename.'</a>';
 
-                    $ffurl = get_file_url("$filearea/$file");
+                if ($candelete) {
+                    $delurl  = "$CFG->wwwroot/mod/assignment/delete.php?id={$this->cm->id}&amp;file=".rawurlencode($filename)."&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset&amp;action=response";
 
-                    $output .= '<a href="'.$ffurl.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.$file.'</a>';
-
-                    if ($candelete) {
-                        $delurl  = "$CFG->wwwroot/mod/assignment/delete.php?id={$this->cm->id}&amp;file=$file&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset&amp;action=response";
-
-                        $output .= '<a href="'.$delurl.'">&nbsp;'
-                                  .'<img title="'.$strdelete.'" src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt=""/></a> ';
-                    }
-
-                    $output .= '&nbsp;';
+                    $output .= '<a href="'.$delurl.'">&nbsp;'
+                              .'<img title="'.$strdelete.'" src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt=""/></a> ';
                 }
-            }
 
+                $output .= '&nbsp;';
+            }
 
             $output = '<div class="responsefiles">'.$output.'</div>';
-
         }
 
         if ($return) {
@@ -549,7 +526,7 @@ class assignment_upload extends assignment_base {
     }
 
     function upload_responsefile() {
-        global $CFG;
+        global $CFG, $USER;
 
         $userid = required_param('userid', PARAM_INT);
         $mode   = required_param('mode', PARAM_ALPHA);
@@ -557,31 +534,28 @@ class assignment_upload extends assignment_base {
 
         $returnurl = "submissions.php?id={$this->cm->id}&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset";
 
-        if (data_submitted() and $this->can_manage_responsefiles()) {
-            $dir = $this->file_area_name($userid).'/responses';
-            check_dir_exists($CFG->dataroot.'/'.$dir, true, true);
-
-            require_once($CFG->dirroot.'/lib/uploadlib.php');
-            $um = new upload_manager('newfile',false,true,$this->course,false,0,true);
-
-            if (!$um->process_file_uploads($dir)) {
-                print_header(get_string('upload'));
-                notify(get_string('uploaderror', 'assignment'));
-                echo $um->get_errors();
-                print_continue($returnurl);
-                print_footer('none');
-                die;
+        $mform = new mod_assignment_upload_response_form(null, $this);
+        if ($mform->get_data() and $this->can_manage_responsefiles()) {
+            $fs = get_file_storage();
+            $filename = $mform->get_new_filename('newfile');
+            if ($filename !== false) {
+                if (!$fs->file_exists($this->context->id, 'assignment_response', $userid, '/', $filename)) {
+                    if ($file = $mform->save_stored_file('newfile', $this->context->id, 'assignment_response', $userid, '/', $filename, false, $USER->id)) {
+                        redirect($returnurl);
+                    }
+                }
             }
         }
-        redirect($returnurl);
+        print_header(get_string('upload'));
+        notify(get_string('uploaderror', 'assignment'));
+        print_continue($returnurl);
+        print_footer('none');
+        die;
     }
 
     function upload_file() {
         global $CFG, $USER, $DB;
 
-        $mode   = optional_param('mode', '', PARAM_ALPHA);
-        $offset = optional_param('offset', 0, PARAM_INT);
-
         $returnurl = 'view.php?id='.$this->cm->id;
 
         $filecount = $this->count_user_files($USER->id);
@@ -595,44 +569,71 @@ class assignment_upload extends assignment_base {
             die;
         }
 
-        $dir = $this->file_area_name($USER->id);
-        check_dir_exists($CFG->dataroot.'/'.$dir, true, true); // better to create now so that student submissions do not block it later
-
-        require_once($CFG->dirroot.'/lib/uploadlib.php');
-        $um = new upload_manager('newfile',false,true,$this->course,false,$this->assignment->maxbytes,true);
-
-        if ($um->process_file_uploads($dir)) {
-            $submission = $this->get_submission($USER->id, true); //create new submission if needed
-            $updated = new object();
-            $updated->id           = $submission->id;
-            $updated->timemodified = time();
-
-            if ($DB->update_record('assignment_submissions', $updated)) {
-                add_to_log($this->course->id, 'assignment', 'upload',
-                        'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
-                $submission = $this->get_submission($USER->id);
-                $this->update_grade($submission);
-                if (!$this->drafts_tracked()) {
-                    $this->email_teachers($submission);
+        $mform = new mod_assignment_upload_file_form('upload.php', $this);
+        if ($mform->get_data()) {
+            $fs = get_file_storage();
+            $filename = $mform->get_new_filename('newfile');
+            if ($filename !== false) {
+                if (!$fs->file_exists($this->context->id, 'assignment_submission', $USER->id, '/', $filename)) {
+                    if ($file = $mform->save_stored_file('newfile', $this->context->id, 'assignment_submission', $USER->id, '/', $filename, false, $USER->id)) {
+                        $submission = $this->get_submission($USER->id, true); //create new submission if needed
+                        $submission->timemodified = time();
+                        if ($DB->update_record('assignment_submissions', $submission)) {
+                            add_to_log($this->course->id, 'assignment', 'upload',
+                                    'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
+                            $this->update_grade($submission);
+                            if (!$this->drafts_tracked()) {
+                                $this->email_teachers($submission);
+                            }
+                            redirect('view.php?id='.$this->cm->id);
+                        } else {
+                            $file->delete();
+                        }
+                    }
                 }
-            } else {
-                $new_filename = $um->get_new_filename();
-                $this->view_header(get_string('upload'));
-                notify(get_string('uploadnotregistered', 'assignment', $new_filename));
-                print_continue($returnurl);
-                $this->view_footer();
-                die;
             }
-            redirect('view.php?id='.$this->cm->id);
         }
+
         $this->view_header(get_string('upload'));
         notify(get_string('uploaderror', 'assignment'));
-        echo $um->get_errors();
         print_continue($returnurl);
         $this->view_footer();
         die;
     }
 
+    function send_file($filearea, $args) {
+        global $CFG, $DB, $USER;
+        require_once($CFG->libdir.'/filelib.php');
+
+        require_login($this->course, false, $this->cm);
+
+        $userid = (int)array_shift($args);
+        $relativepath = '/'.implode('/', $args);
+        $fullpath = $this->context->id.$filearea.$userid.$relativepath;
+
+        $fs = get_file_storage();
+
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+            return false;
+        }
+
+        if ($filearea === 'assignment_submission') {
+            if ($USER->id != $userid and !has_capability('mod/assignment:grade', $this->context)) {
+                return false;
+            }
+
+        } else if ($filearea === 'assignment_response') {
+            if ($USER->id != $userid and !has_capability('mod/assignment:grade', $this->context)) {
+                return false;
+            }
+
+        } else {
+            return false;
+        }
+
+        send_stored_file($file, 0, 0, true); // downlaod MUST be forced - security!
+    }
+
     function finalize() {
         global $USER, $DB;
 
@@ -777,10 +778,9 @@ class assignment_upload extends assignment_base {
             die;
         }
 
-        $dir = $this->file_area_name($userid).'/responses';
-        $filepath = $CFG->dataroot.'/'.$dir.'/'.$file;
-        if (file_exists($filepath)) {
-            if (@unlink($filepath)) {
+        $fs = get_file_storage();
+        if ($file = $fs->get_file($this->context->id, 'assignment_submission', $userid, '/', $file)) {
+            if ($file->delete()) {
                 redirect($returnurl);
             }
         }
@@ -824,7 +824,6 @@ class assignment_upload extends assignment_base {
             $this->view_footer();
             die;
         }
-        $dir = $this->file_area_name($userid);
 
         if (!data_submitted() or !$confirm) {
             $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'sesskey'=>sesskey(), 'mode'=>$mode, 'offset'=>$offset);
@@ -843,16 +842,13 @@ class assignment_upload extends assignment_base {
             die;
         }
 
-        $filepath = $CFG->dataroot.'/'.$dir.'/'.$file;
-        if (file_exists($filepath)) {
-            if (@unlink($filepath)) {
-                $updated = new object();
-                $updated->id = $submission->id;
-                $updated->timemodified = time();
-                if ($DB->update_record('assignment_submissions', $updated)) {
+        $fs = get_file_storage();
+        if ($file = $fs->get_file($this->context->id, 'assignment_submission', $userid, '/', $file)) {
+            if ($file->delete()) {
+                $submission->timemodified = time();
+                if ($DB->update_record('assignment_submissions', $submission)) {
                     add_to_log($this->course->id, 'assignment', 'upload', //TODO: add delete action to log
                             'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
-                    $submission = $this->get_submission($userid);
                     $this->update_grade($submission);
                 }
                 redirect($returnurl);
@@ -997,37 +993,10 @@ class assignment_upload extends assignment_base {
         return (boolean)$this->assignment->var2;
     }
 
-    /**
-     * Count the files uploaded by a given user
-     *
-     * @param $userid int The user id
-     * @return int
-     */
-    function count_user_files($userid) {
-        global $CFG;
-
-        $filearea = $this->file_area_name($userid);
-
-        if ( is_dir($CFG->dataroot.'/'.$filearea) && $basedir = $this->file_area($userid)) {
-            if ($files = get_directory_list($basedir, 'responses')) {
-                return count($files);
-            }
-        }
-        return 0;
-    }
-
     function count_responsefiles($userid) {
-        global $CFG;
-
-        $filearea = $this->file_area_name($userid).'/responses';
-
-        if ( is_dir($CFG->dataroot.'/'.$filearea) && $basedir = $this->file_area($userid)) {
-            $basedir .= '/responses';
-            if ($files = get_directory_list($basedir)) {
-                return count($files);
-            }
-        }
-        return 0;
+        $fs = get_file_storage();
+        $files = $fs->get_area_files($this->context->id, 'assignment_response', $userid, "id", false);
+        return count($files);
     }
 
     function setup_elements(&$mform) {
@@ -1078,7 +1047,7 @@ class assignment_upload extends assignment_base {
 
 class mod_assignment_upload_notes_form extends moodleform {
     function definition() {
-        $mform =& $this->_form;
+        $mform = $this->_form;
 
         // visible elements
         $mform->addElement('htmleditor', 'text', get_string('notes', 'assignment'), array('cols'=>85, 'rows'=>30));
@@ -1089,13 +1058,40 @@ class mod_assignment_upload_notes_form extends moodleform {
         $mform->addElement('hidden', 'id', 0);
         $mform->setType('id', PARAM_INT);
         $mform->addElement('hidden', 'action', 'savenotes');
-        $mform->setType('id', PARAM_ALPHA);
+        $mform->setType('action', PARAM_ALPHA);
 
         // buttons
         $this->add_action_buttons();
     }
 }
 
+class mod_assignment_upload_response_form extends moodleform {
+    function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+
+        // visible elements
+        $mform->addElement('file', 'newfile', get_string('uploadafile'));
+
+        // hidden params
+        $mform->addElement('hidden', 'id', $instance->cm->id);
+        $mform->setType('id', PARAM_INT);
+        $mform->addElement('hidden', 'action', 'uploadresponse');
+        $mform->setType('action', PARAM_ALPHA);
+        $mform->addElement('hidden', 'mode');
+        $mform->setType('mode', PARAM_ALPHA);
+        $mform->addElement('hidden', 'offset');
+        $mform->setType('offset', PARAM_INT);
+        $mform->addElement('hidden', 'forcerefresh');
+        $mform->setType('forcerefresh', PARAM_INT);
+        $mform->addElement('hidden', 'userid');
+        $mform->setType('userid', PARAM_INT);
+
+        // buttons
+        $this->add_action_buttons(false, get_string('uploadthisfile'));
+    }
+}
+
 
 
 ?>
index 886117b..96d6ee1 100644 (file)
@@ -8,25 +8,20 @@ class assignment_uploadsingle extends assignment_base {
 
 
     function print_student_answer($userid, $return=false){
-           global $CFG, $USER;
+        global $CFG, $USER;
 
-        $filearea = $this->file_area_name($userid);
+        $fs = get_file_storage();
+        $browser = get_file_browser();
 
-        $output = '';
+        if ($files = $fs->get_area_files($this->context->id, 'assignment_submission', $userid, "timemodified", false)) {
 
-        if ($basedir = $this->file_area($userid)) {
-            if ($files = get_directory_list($basedir)) {
-                require_once($CFG->libdir.'/filelib.php');
-                foreach ($files as $key => $file) {
-
-                    $icon = mimeinfo('icon', $file);
-                    $ffurl = get_file_url("$filearea/$file");
-
-                    //died right here
-                    //require_once($ffurl);
-                    $output = '<img src="'.$CFG->pixpath.'/f/'.$icon.'" class="icon" alt="'.$icon.'" />'.
-                            '<a href="'.$ffurl.'" >'.$file.'</a><br />';
-                }
+            foreach ($files as $file) {
+                $filename = $file->get_filename();
+                $found = true;
+                $mimetype = $file->get_mimetype();
+                $icon = mimeinfo_from_type('icon', $mimetype);
+                $path = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename);
+                $output .= '<a href="'.$path.'" ><img class="icon" src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.s($filename).'</a><br />';
             }
         }
 
@@ -74,24 +69,8 @@ class assignment_uploadsingle extends assignment_base {
 
 
     function view_upload_form() {
-        global $CFG;
-        $struploadafile = get_string("uploadafile");
-
-        $maxbytes = $this->assignment->maxbytes == 0 ? $this->course->maxbytes : $this->assignment->maxbytes;
-        $strmaxsize = get_string('maxsize', '', display_size($maxbytes));
-
-        echo '<div style="text-align:center">';
-        echo '<form enctype="multipart/form-data" method="post" '.
-             "action=\"$CFG->wwwroot/mod/assignment/upload.php\">";
-        echo '<fieldset class="invisiblefieldset">';
-        echo "<p>$struploadafile ($strmaxsize)</p>";
-        echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
-        require_once($CFG->libdir.'/uploadlib.php');
-        upload_print_form_fragment(1,array('newfile'),false,null,0,$this->assignment->maxbytes,false);
-        echo '<input type="submit" name="save" value="'.get_string('uploadthisfile').'" />';
-        echo '</fieldset>';
-        echo '</form>';
-        echo '</div>';
+        $mform = new mod_assignment_upload_file_form('upload.php', $this);
+        $mform->display();
     }
 
 
@@ -112,46 +91,32 @@ class assignment_uploadsingle extends assignment_base {
                 }
             }
 
-            $dir = $this->file_area_name($USER->id);
-
-            require_once($CFG->dirroot.'/lib/uploadlib.php');
-            $um = new upload_manager('newfile',true,false,$this->course,false,$this->assignment->maxbytes);
-            if ($um->process_file_uploads($dir)) {
-                $newfile_name = $um->get_new_filename();
-                if ($submission) {
-                    $submission->timemodified = time();
-                    $submission->numfiles     = 1;
-                    $submission->submissioncomment = $submission->submissioncomment;
-                    unset($submission->data1);  // Don't need to update this.
-                    unset($submission->data2);  // Don't need to update this.
-                    if ($DB->update_record("assignment_submissions", $submission)) {
-                        add_to_log($this->course->id, 'assignment', 'upload',
-                                'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
-                        $submission = $this->get_submission($USER->id);
-                        $this->update_grade($submission);
-                        $this->email_teachers($submission);
-                        print_heading(get_string('uploadedfile'));
-                    } else {
-                        notify(get_string("uploadfailnoupdate", "assignment"));
-                    }
-                } else {
-                    $newsubmission = $this->prepare_new_submission($USER->id);
-                    $newsubmission->timemodified = time();
-                    $newsubmission->numfiles = 1;
-                    if ($DB->insert_record('assignment_submissions', $newsubmission)) {
-                        add_to_log($this->course->id, 'assignment', 'upload',
-                                'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
-                        $submission = $this->get_submission($USER->id);
-                        $this->update_grade($submission);
-                        $this->email_teachers($newsubmission);
-                        print_heading(get_string('uploadedfile'));
-                    } else {
-                        notify(get_string("uploadnotregistered", "assignment", $newfile_name) );
+            $mform = new mod_assignment_upload_file_form('upload.php', $this);
+            if ($mform->get_data()) {
+                $fs = get_file_storage();
+                $filename = $mform->get_new_filename('newfile');
+                if ($filename !== false) {
+                    $fs->delete_area_files($this->context->id, 'assignment_submission', $USER->id);
+                    if ($file = $mform->save_stored_file('newfile', $this->context->id, 'assignment_submission', $USER->id, '/', $filename, false, $USER->id)) {
+                        $submission = $this->get_submission($USER->id, true); //create new submission if needed
+                        $submission->timemodified = time();
+                        $submission->numfiles     = 1;
+                        if ($DB->update_record('assignment_submissions', $submission)) {
+                            add_to_log($this->course->id, 'assignment', 'upload',
+                                    'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
+                            $this->update_grade($submission);
+                            $this->email_teachers($submission);
+                            print_heading(get_string('uploadedfile'));
+                            redirect('view.php?id='.$this->cm->id);
+                        } else {
+                            notify(get_string("uploadnotregistered", "assignment", $newfile_name) );
+                            $file->delete();
+                        }
                     }
                 }
+            } else {
+                notify(get_string("uploaderror", "assignment")); //submitting not allowed!
             }
-        } else {
-            notify(get_string("uploaderror", "assignment")); //submitting not allowed!
         }
 
         print_continue('view.php?id='.$this->cm->id);
@@ -183,6 +148,34 @@ class assignment_uploadsingle extends assignment_base {
         return true;
     }
 
+    function send_file($filearea, $args) {
+        global $CFG, $DB, $USER;
+        require_once($CFG->libdir.'/filelib.php');
+
+        require_login($this->course, false, $this->cm);
+
+        $userid = (int)array_shift($args);
+        $relativepath = '/'.implode('/', $args);
+        $fullpath = $this->context->id.$filearea.$userid.$relativepath;
+
+        $fs = get_file_storage();
+
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+            return false;
+        }
+
+        if ($filearea === 'assignment_submission') {
+            if ($USER->id != $userid and !has_capability('mod/assignment:grade', $this->context)) {
+                return false;
+            }
+
+        } else {
+            return false;
+        }
+
+        send_stored_file($file, 0, 0, true); // downlaod MUST be forced - security!
+    }
+
 }
 
 ?>
index 2b4059c..d937f18 100644 (file)
@@ -33,7 +33,7 @@
     require_login($course->id, false, $cm);
 
 /// Load up the required assignment code
-    require($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php');
+    require_once($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php');
     $assignmentclass = 'assignment_'.$assignment->assignmenttype;
     $assignmentinstance = new $assignmentclass($cm->id, $assignment, $cm, $course);
 
index dab1a05..2d169bc 100644 (file)
@@ -5,7 +5,7 @@
 //  This fragment is called by /admin/index.php
 ////////////////////////////////////////////////////////////////////////////////
 
-$module->version  = 2008072401;
+$module->version  = 2008073000;
 $module->requires = 2008072401;  // Requires this Moodle version
 $module->cron     = 60;
 
diff --git a/pluginfile.php b/pluginfile.php
new file mode 100644 (file)
index 0000000..21962a5
--- /dev/null
@@ -0,0 +1,181 @@
+<?php  // $Id$
+
+    require_once('config.php');
+    require_once('lib/filelib.php');
+
+    // disable moodle specific debug messages
+    disable_debugging();
+
+    $relativepath = get_file_argument('file.php');
+    $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
+
+    // relative path must start with '/'
+    if (!$relativepath) {
+        print_error('invalidargorconf');
+    } else if ($relativepath{0} != '/') {
+        print_error('pathdoesnotstartslash');
+    }
+
+    // extract relative path components
+    $args = explode('/', ltrim($relativepath, '/'));
+
+    if (count($args) == 0) { // always at least user id
+        print_error('invalidarguments');
+    }
+
+    $contextid = (int)array_shift($args);
+    $filearea = array_shift($args);
+
+    $context = get_context_instance_by_id($contextid);
+    $fs = get_file_storage();
+
+
+    if ($context->contextlevel == CONTEXT_SYSTEM) {
+        if ($filearea === 'blog') {
+
+            if (empty($CFG->bloglevel)) {
+                print_error('siteblogdisable', 'blog');
+            }
+            if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL) {
+                require_login();
+                if (isguestuser()) {
+                    print_error('noguest');
+                }
+                if ($CFG->bloglevel == BLOG_USER_LEVEL) {
+                    if ($USER->id != $entry->userid) {
+                        not_found();
+                    }
+                }
+            }
+            $entryid = (int)array_shift($args);
+            if (!$entry = $DB->get_record('post', array('module'=>'blog', 'id'=>$entryid))) {
+                not_found();
+            }
+            if ('publishstate' === 'public') {
+                if ($CFG->forcelogin) {
+                    require_login();
+                }
+
+            } else if ('publishstate' === 'site') {
+                require_login();
+                //ok
+            } else if ('publishstate' === 'draft') {
+                require_login();
+                if ($USER->id != $entry->userid) {
+                    not_found();
+                }
+            }
+
+            //TODO: implement shared course and shared group access
+
+            $relativepath = '/'.implode('/', $args);
+            $fullpath = $context->id.'blog'.$entryid.$relativepath;
+
+            if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+                not_found();
+            }
+
+            send_stored_file($file, 10*60, 0, true); // downlaod MUST be forced - security!
+
+        } else {
+            not_found();
+        }
+
+
+    } else if ($context->contextlevel == CONTEXT_USER) {
+        not_found();
+
+
+    } else if ($context->contextlevel == CONTEXT_COURSECAT) {
+        if ($filearea !== 'intro') {
+            not_found();
+        }
+
+        if ($CFG->forcelogin) {
+            // no login necessary - unless login forced everywhere
+            require_login();
+        }
+
+        $relativepath = '/'.implode('/', $args);
+        $fullpath = $context->id.'intro0'.$relativepath;
+
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->get_filename() == '.') {
+            not_found();
+        }
+
+        session_write_close(); // unlock session during fileserving
+        send_stored_file($file, 60*60, 0, $forcedownload);
+
+
+    } else if ($context->contextlevel == CONTEXT_COURSE) {
+        if ($filearea !== 'intro' and $filearea !== 'backup') {
+            not_found();
+        }
+
+        if (!$course = $DB->get_record('course', array('id'=>$context->instanceid))) {
+            print_error('invalidcourseid');
+        }
+
+        if ($filearea === 'backup') {
+            require_login($course);
+            require_capability('moodle/site:backupdownload', $context);
+        } else {
+            if ($CFG->forcelogin) {
+                require_login();
+            }
+        }
+
+        $relativepath = '/'.implode('/', $args);
+        $fullpath = $context->id.'intro0'.$relativepath;
+
+        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+            not_found();
+        }
+
+        session_write_close(); // unlock session during fileserving
+        send_stored_file($file, 60*60, 0, $forcedownload);
+
+
+    } else if ($context->contextlevel == CONTEXT_MODULE) {
+        
+        if (!$coursecontext = get_context_instance_by_id(get_parent_contextid($context))) {
+            not_found();
+        }
+
+        if (!$course = $DB->get_record('course', array('id'=>$coursecontext->instanceid))) {
+            not_found();
+        }
+        $modinfo = get_fast_modinfo($course);
+        if (empty($modinfo->cms[$context->instanceid])) {
+            not_found();
+        }
+
+        $cminfo = $modinfo->cms[$context->instanceid];
+        $modname = $cminfo->modname;
+        $libfile = "$CFG->dirroot/mod/$modname/lib.php";
+        if (file_exists($libfile)) {
+            require_once($libfile);
+            $filefunction = $modname.'_pluginfile';
+            if (function_exists($filefunction)) {
+                if ($filefunction($course, $cminfo, $context, $filearea, $args) !== false) {
+                    die;
+                }
+            }
+        }
+        not_found();
+
+    } else if ($context->contextlevel == CONTEXT_BLOCK) {
+        //not supported yet
+        not_found();
+
+
+    } else {
+        not_found();
+    }
+
+
+    function not_found() {
+        global $CFG;
+        header('HTTP/1.0 404 not found');
+        print_error('filenotfound', 'error', $CFG->wwwroot.'/'); //this is not displayed on IIS??
+    }
index 0f08437..8d849aa 100644 (file)
@@ -9,7 +9,6 @@ class user_edit_form extends moodleform {
         global $CFG, $COURSE;
 
         $mform =& $this->_form;
-        $this->set_upload_manager(new upload_manager('imagefile', false, false, null, false, 0, true, true, false));
         //Accessibility: "Required" is bad legend text.
         $strgeneral  = get_string('general');
         $strrequired = get_string('required');
index e0c9123..2d16192 100644 (file)
@@ -9,7 +9,6 @@ class user_editadvanced_form extends moodleform {
         global $USER, $CFG, $COURSE;
 
         $mform =& $this->_form;
-        $this->set_upload_manager(new upload_manager('imagefile', false, false, null, false, 0, true, true, false));
         //Accessibility: "Required" is bad legend text.
         $strgeneral  = get_string('general');
         $strrequired = get_string('required');
index 1c97f65..ba31678 100644 (file)
@@ -33,15 +33,17 @@ function useredit_update_user_preference($usernew) {
     }
 }
 
-function useredit_update_picture(&$usernew, &$userform) {
+function useredit_update_picture(&$usernew, $userform) {
     global $CFG, $DB;
 
     if (isset($usernew->deletepicture) and $usernew->deletepicture) {
         $location = make_user_directory($usernew->id, true);
         @remove_dir($location);
         $DB->set_field('user', 'picture', 0, array('id'=>$usernew->id));
-    } else if ($usernew->picture = save_profile_image($usernew->id, $userform->get_um(), 'user')) {
-        $DB->set_field('user', 'picture', 1, array('id'=>$usernew->id));
+
+    } else if ($userform->get_new_filename('imagefile')) {
+        $usernew->picture = (int)save_profile_image($usernew->id, $userform, 'user', 'imagefile');
+        $DB->set_field('user', 'picture', $usernew->picture, array('id'=>$usernew->id));
     }
 }
 
diff --git a/userfile.php b/userfile.php
new file mode 100644 (file)
index 0000000..368dcdc
--- /dev/null
@@ -0,0 +1,71 @@
+<?php  // $Id$
+
+    require_once('config.php');
+    require_once('lib/filelib.php');
+
+    require_login();
+    if (isguestuser()) {
+        print_error('noguest');
+    }
+
+    // disable moodle specific debug messages
+    disable_debugging();
+
+    $relativepath = get_file_argument('file.php');
+    $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
+
+    // relative path must start with '/'
+    if (!$relativepath) {
+        print_error('invalidargorconf');
+    } else if ($relativepath{0} != '/') {
+        print_error('pathdoesnotstartslash');
+    }
+
+    // extract relative path components
+    $args = explode('/', ltrim($relativepath, '/'));
+
+    if (count($args) == 0) { // always at least user id
+        print_error('invalidarguments');
+    }
+
+    $contextid = (int)array_shift($args);
+    $filearea = array_shift($args);
+
+    $context = get_context_instance_by_id($contextid);
+    if ($context->contextlevel != CONTEXT_USER) {
+        print_error('invalidarguments');
+    }
+
+    $userid = $context->instanceid;
+    if ($USER->id != $userid) {
+        print_error('invaliduserid');
+    }
+
+    switch ($filearea) {
+        case 'private': $itemid = 0; $forcedownload = true; break;
+        case 'draft'  : $itemid = (int)array_shift($args); break;
+        default:        not_found();
+    }
+
+    $relativepath = '/'.implode('/', $args);
+
+
+    $fs = get_file_storage();
+
+    $fullpath = $context->id.$filearea.$itemid.$relativepath;
+
+    if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->get_filename() == '.') {
+        not_found();
+    }
+
+    // ========================================
+    // finally send the file
+    // ========================================
+    session_write_close(); // unlock session during fileserving
+    send_stored_file($file, 0, false, $forcedownload);
+
+    function not_found() {
+        global $CFG;
+        header('HTTP/1.0 404 not found');
+        print_error('filenotfound', 'error', $CFG->wwwroot.'/'); //this is not displayed on IIS??
+    }
index f55f818..933a743 100644 (file)
@@ -6,7 +6,7 @@
 // This is compared against the values stored in the database to determine
 // whether upgrades should be performed (see lib/db/*.php)
 
-    $version = 2008073104;  // YYYYMMDD   = date of the last version bump
+    $version = 2008073114;  // YYYYMMDD   = date of the last version bump
                             //         XX = daily increments
 
     $release = '2.0 dev (Build: 20080731)';  // Human-friendly version name