Merge branch 'MDL-33173-master' of git://github.com/FMCorz/moodle
authorDan Poltawski <dan@moodle.com>
Fri, 25 May 2012 01:57:35 +0000 (09:57 +0800)
committerDan Poltawski <dan@moodle.com>
Fri, 25 May 2012 01:57:35 +0000 (09:57 +0800)
75 files changed:
admin/tool/assignmentupgrade/batchupgrade.php
admin/tool/assignmentupgrade/index.php
admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php
admin/tool/assignmentupgrade/listnotupgraded.php
admin/tool/assignmentupgrade/locallib.php
admin/tool/assignmentupgrade/module.js
admin/tool/assignmentupgrade/paginationform.php [new file with mode: 0644]
admin/tool/assignmentupgrade/renderer.php
admin/tool/assignmentupgrade/styles.css
admin/tool/assignmentupgrade/upgradableassignmentstable.php
admin/tool/assignmentupgrade/upgradesingle.php
admin/tool/assignmentupgrade/upgradesingleconfirm.php
blog/index.php
comment/comment.js
course/completion.php
course/dnduploadlib.php
course/format/weeks/format.js
files/renderer.php
lib/completion/completion_aggregation.php
lib/completion/completion_completion.php
lib/completion/completion_criteria.php
lib/completion/completion_criteria_activity.php
lib/completion/completion_criteria_completion.php
lib/completion/completion_criteria_course.php
lib/completion/completion_criteria_date.php
lib/completion/completion_criteria_duration.php
lib/completion/completion_criteria_grade.php
lib/completion/cron.php
lib/completion/data_object.php
lib/completionlib.php
lib/cronlib.php
lib/db/install.php
lib/filelib.php
lib/filestorage/file_storage.php
lib/filestorage/tests/file_storage_test.php
lib/form/editor.php
lib/form/filepicker.php
lib/form/yui/checkboxcontroller/checkboxcontroller.js
lib/outputlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/pluginlib.php
lib/tests/filelib_test.php
mod/assign/backup/moodle2/backup_assign_stepslib.php
mod/assign/db/install.xml
mod/assign/db/messages.php
mod/assign/db/upgrade.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/mod_form.php
mod/assign/settings.php
mod/assign/upgradelib.php
mod/assign/version.php
mod/data/field/picture/field.class.php
mod/data/field/textarea/field.class.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/resource/locallib.php
mod/wiki/edit_form.php
pix/f/folder-128.png
pix/f/folder-24.png
pix/f/folder-32.png
pix/f/folder-48.png
pix/f/folder-64.png
pix/f/folder-open-128.png
pix/f/folder-open-24.png
pix/f/folder-open-32.png
pix/f/folder-open-48.png
pix/f/folder-open-64.png
pix/f/folder-open.png
pix/f/folder.png
repository/upload/lib.php
theme/base/style/core.css
theme/base/style/filemanager.css

index 20ebf5c..31bca24 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+define('NO_OUTPUT_BUFFERING', true);
+
 require_once(dirname(__FILE__) . '/../../../config.php');
-require_once(dirname(__FILE__) . '/locallib.php');
-require_once(dirname(__FILE__) . '/upgradableassignmentstable.php');
-require_once(dirname(__FILE__) . '/upgradableassignmentsbatchform.php');
 require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/locallib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/upgradableassignmentstable.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php');
 
 require_sesskey();
 
 // admin_externalpage_setup calls require_login and checks moodle/site:config
 admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('batchupgrade'));
+
+$PAGE->set_pagelayout('maintenance');
 $PAGE->navbar->add(get_string('batchupgrade', 'tool_assignmentupgrade'));
 
 $renderer = $PAGE->get_renderer('tool_assignmentupgrade');
@@ -41,7 +45,26 @@ if (!$confirm) {
     print_error('invalidrequest');
     die();
 }
-$result = tool_assignmentupgrade_upgrade_multiple_assignments(optional_param('upgradeall', 0, PARAM_BOOL),
-                                                explode(',', optional_param('selected', '', PARAM_TEXT)));
+raise_memory_limit(MEMORY_EXTRA);
+session_get_instance()->write_close(); // release session
+
+echo $renderer->header();
+echo $renderer->heading(get_string('batchupgrade', 'tool_assignmentupgrade'));
+
+$current = 0;
+if (optional_param('upgradeall', false, PARAM_BOOL)) {
+    $assignmentids = tool_assignmentupgrade_load_all_upgradable_assignmentids();
+} else {
+    $assignmentids = explode(',', optional_param('selected', '', PARAM_TEXT));
+}
+$total = count($assignmentids);
+
+foreach ($assignmentids as $assignmentid) {
+    list($summary, $success, $log) = tool_assignmentupgrade_upgrade_assignment($assignmentid);
+    $current += 1;
+    echo $renderer->heading(get_string('upgradeprogress', 'tool_assignmentupgrade', array('current'=>$current, 'total'=>$total)), 3);
+    echo $renderer->convert_assignment_result($summary, $success, $log);
+}
 
-echo $renderer->convert_multiple_assignments_result($result);
+echo $renderer->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
+echo $renderer->footer();
index bb7ff49..f4cd179 100644 (file)
@@ -34,8 +34,8 @@
  */
 
 require_once(dirname(__FILE__) . '/../../../config.php');
-require_once(dirname(__FILE__) . '/locallib.php');
 require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/locallib.php');
 
 // admin_externalpage_setup calls require_login and checks moodle/site:config
 admin_externalpage_setup('assignmentupgrade');
@@ -47,4 +47,4 @@ $actions = array();
 $header = get_string('pluginname', 'tool_assignmentupgrade');
 $actions[] = tool_assignmentupgrade_action::make('listnotupgraded');
 
-echo $renderer->index_page($header, $actions);
\ No newline at end of file
+echo $renderer->index_page($header, $actions);
index 3b3734b..e9a61dc 100644 (file)
@@ -26,6 +26,7 @@ $string['areyousure'] = 'Are you sure?';
 $string['areyousuremessage'] = 'Are you sure you want to upgrade the assignment "{$a->name}"?';
 $string['assignmentid'] = 'Assignment ID';
 $string['assignmentnotfound'] = 'Assignment could not be found (id={$a})';
+$string['assignmentsperpage'] = 'Assignments per page';
 $string['assignmenttype'] = 'Assignment type';
 $string['backtoindex'] = 'Back to index';
 $string['batchoperations'] = 'Batch operations';
@@ -43,6 +44,7 @@ $string['pluginname'] = 'Assignment upgrade helper';
 $string['select'] = 'Select';
 $string['submissions'] = 'Submissions';
 $string['supported'] = 'Upgrade';
+$string['updatetable'] = 'Update table';
 $string['unknown'] = 'Unknown';
 $string['upgradeassignmentsummary'] = 'Upgrade assignment: {$a->name} (Course: {$a->shortname})';
 $string['upgradeassignmentsuccess'] = 'Result: Upgrade successful';
@@ -52,5 +54,6 @@ $string['upgradeselected'] = 'Upgrade selected assignments';
 $string['upgradeselectedcount'] = 'Upgrade {$a} selected assignments?';
 $string['upgradeall'] = 'Upgrade all assignments';
 $string['upgradeallconfirm'] = 'Upgrade all assignments?';
+$string['upgradeprogress'] = 'Upgrade assignment {$a->current} of {$a->total}';
 $string['upgradesingle'] = 'Upgrade single assignment';
 $string['viewcourse'] = 'View the course with the converted assignment';
index 37605a5..b03883a 100644 (file)
  */
 
 require_once(dirname(__FILE__) . '/../../../config.php');
-require_once(dirname(__FILE__) . '/locallib.php');
-require_once(dirname(__FILE__) . '/upgradableassignmentstable.php');
-require_once(dirname(__FILE__) . '/upgradableassignmentsbatchform.php');
 require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/locallib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/upgradableassignmentstable.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/paginationform.php');
 
 // admin_externalpage_setup calls require_login and checks moodle/site:config
 admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('listnotupgraded'));
@@ -34,16 +35,26 @@ $PAGE->navbar->add(get_string('listnotupgraded', 'tool_assignmentupgrade'));
 
 $renderer = $PAGE->get_renderer('tool_assignmentupgrade');
 
-$perpage = get_user_preferences('tool_assignmentupgrade_perpage', 5);
+$perpage = optional_param('perpage', 0, PARAM_INT);
+if (!$perpage) {
+    $perpage = get_user_preferences('tool_assignmentupgrade_perpage', 100);
+} else {
+    set_user_preference('tool_assignmentupgrade_perpage', $perpage);
+}
 $assignments = new tool_assignmentupgrade_assignments_table($perpage);
 
 $batchform = new tool_assignmentupgrade_batchoperations_form();
 $data = $batchform->get_data();
+
 if ($data && $data->selectedassignments != '' || $data && isset($data->upgradeall)) {
     require_sesskey();
     echo $renderer->confirm_batch_operation_page($data);
 } else {
-    echo $renderer->assignment_list_page($assignments, $batchform);
+    $paginationform = new tool_assignmentupgrade_pagination_form();
+    $pagedata = new stdClass();
+    $pagedata->perpage = $perpage;
+    $paginationform->set_data($pagedata);
+    echo $renderer->assignment_list_page($assignments, $batchform, $paginationform);
 }
 
 
index 5b81485..6ea1883 100644 (file)
@@ -170,54 +170,33 @@ function tool_assignmentupgrade_load_all_upgradable_assignmentids() {
 
 
 /**
- * Convert a list of assignments from the old format to the new one.
- * @param bool $upgradeall - Upgrade all possible assignments
- * @param array $assignmentids An array of assignment ids to upgrade
- * @return array of $entry['assignmentsummary' => (result from tool_assignmentupgrade_get_assignment)
- *                  $entry['success'] => boolean
- *                  $entry['log'] => string - upgrade log
+ * Upgrade a single assignment. This is used by both upgrade single and upgrade batch
+ *
+ * @param int $assignmentid - The assignment id to upgrade
+ * @return array(string, boolean, string) -
+ *                  The array contains
+ *                      - the assignment summary (returned by tool_assignmentupgrade_get_assignment)
+ *                      - success
+ *                      - the upgrade log
  */
-function tool_assignmentupgrade_upgrade_multiple_assignments($upgradeall, $assignmentids) {
+function tool_assignmentupgrade_upgrade_assignment($assignmentid) {
     global $CFG;
-    require_once($CFG->dirroot . '/mod/assign/locallib.php');
     require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
-    $upgrades = array();
-
-    if ($upgradeall) {
-        $assignmentids = tool_assignmentupgrade_load_all_upgradable_assignmentids();
-    }
 
     $assignment_upgrader = new assign_upgrade_manager();
-    foreach ($assignmentids as $assignmentid) {
-        $info = tool_assignmentupgrade_get_assignment($assignmentid);
-        if ($info) {
-            $log = '';
-            $success = $assignment_upgrader->upgrade_assignment($assignmentid, $log);
-        } else {
-            $success = false;
-            $log = get_string('assignmentnotfound', 'tool_assignmentupgrade', $assignmentid);
-            $info = new stdClass();
-            $info->name = get_string('unknown', 'tool_assignmentupgrade');
-            $info->shortname = get_string('unknown', 'tool_assignmentupgrade');
-        }
-
-        $upgrades[] = array('assignmentsummary'=>$info, 'success'=>$success, 'log'=>$log);
+    $info = tool_assignmentupgrade_get_assignment($assignmentid);
+    if ($info) {
+        $log = '';
+        $success = $assignment_upgrader->upgrade_assignment($assignmentid, $log);
+    } else {
+        $success = false;
+        $log = get_string('assignmentnotfound', 'tool_assignmentupgrade', $assignmentid);
+        $info = new stdClass();
+        $info->name = get_string('unknown', 'tool_assignmentupgrade');
+        $info->shortname = get_string('unknown', 'tool_assignmentupgrade');
     }
-    return $upgrades;
-}
 
-/**
- * Convert a single assignment from the old format to the new one.
- * @param stdClass $assignmentinfo An object containing information about this class
- * @param string $log This gets appended to with the details of the conversion process
- * @return boolean This is the overall result (true/false)
- */
-function tool_assignmentupgrade_upgrade_assignment($assignmentinfo, &$log) {
-    global $CFG;
-    require_once($CFG->dirroot . '/mod/assign/locallib.php');
-    require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
-    $assignment_upgrader = new assign_upgrade_manager();
-    return $assignment_upgrader->upgrade_assignment($assignmentinfo->id, $log);
+    return array($info, $success, $log);
 }
 
 /**
index 829f99a..edee839 100644 (file)
@@ -61,6 +61,11 @@ M.tool_assignmentupgrade = {
             }
         });
 
+        var perpage = Y.one('#id_perpage');
+        perpage.on('change', function(e) {
+            window.onbeforeunload = null;
+            Y.one('.tool_assignmentupgrade_paginationform form').submit();
+        });
 
     }
 }
diff --git a/admin/tool/assignmentupgrade/paginationform.php b/admin/tool/assignmentupgrade/paginationform.php
new file mode 100644 (file)
index 0000000..3efb8d4
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains the forms to create and edit an instance of this module
+ *
+ * @package   tool_assignmentupgrade
+ * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
+
+
+/** Include formslib.php */
+require_once ($CFG->libdir.'/formslib.php');
+
+/**
+ * Assignment upgrade table display options
+ *
+ * @package   tool_assignmentupgrade
+ * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_pagination_form extends moodleform {
+    /**
+     * Define this form - called from the parent constructor
+     */
+    function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+
+        $mform->addElement('header', 'general', get_string('assignmentsperpage', 'tool_assignmentupgrade'));
+        // visible elements
+        $options = array(10=>'10', 20=>'20', 50=>'50', 100=>'100');
+        $mform->addElement('select', 'perpage', get_string('assignmentsperpage', 'assign'), $options);
+
+        // hidden params
+        $mform->addElement('hidden', 'action', 'saveoptions');
+        $mform->setType('action', PARAM_ALPHA);
+
+        // buttons
+        $this->add_action_buttons(false, get_string('updatetable', 'tool_assignmentupgrade'));
+    }
+}
+
index 16c013c..ddb8cfa 100644 (file)
@@ -112,9 +112,10 @@ class tool_assignmentupgrade_renderer extends plugin_renderer_base {
      * Render the list of assignments that still need to be upgraded page.
      * @param tool_assignmentupgrade_assignments_table $assignments of data about assignments.
      * @param tool_assignmentupgrade_batchoperations_form $batchform Submitted form with list of assignments to upgrade
+     * @param tool_assignmentupgrade_pagination_form $paginationform Form which contains the preferences for paginating the table
      * @return string html to output.
      */
-    public function assignment_list_page(tool_assignmentupgrade_assignments_table $assignments, tool_assignmentupgrade_batchoperations_form $batchform) {
+    public function assignment_list_page(tool_assignmentupgrade_assignments_table $assignments, tool_assignmentupgrade_batchoperations_form $batchform, tool_assignmentupgrade_pagination_form $paginationform) {
         $output = '';
         $output .= $this->header();
         $this->page->requires->js_init_call('M.tool_assignmentupgrade.init_upgrade_table', array());
@@ -126,6 +127,10 @@ class tool_assignmentupgrade_renderer extends plugin_renderer_base {
 
         $output .= $this->container_start('tool_assignmentupgrade_upgradetable');
 
+        $output .= $this->container_start('tool_assignmentupgrade_paginationform');
+        $output .= $this->moodleform($paginationform);
+        $output .= $this->container_end();
+
         $output .= $this->flexible_table($assignments, $assignments->get_rows_per_page(), true);
         $output .= $this->container_end();
 
@@ -140,43 +145,6 @@ class tool_assignmentupgrade_renderer extends plugin_renderer_base {
         return $output;
     }
 
-    /**
-     * Render the result of an assignment conversion
-     * @param array $assignments - An array of arrays with keys $entry['assignmentsummary', 'success', 'log']
-     *                            See convert_assignment_result for more description of these keys.
-     * @return string html to output.
-     */
-    public function convert_multiple_assignments_result($assignments) {
-        $output = '';
-        $output .= $this->header();
-        $output .= $this->heading(get_string('batchupgrade', 'tool_assignmentupgrade'));
-
-        foreach ($assignments as $assignment) {
-            $assignmentsummary = $assignment['assignmentsummary'];
-            $success = $assignment['success'];
-            $log = $assignment['log'];
-
-            $output .= $this->heading(get_string('upgradeassignmentsummary', 'tool_assignmentupgrade', $assignmentsummary), 5);
-            if ($success) {
-                $output .= $this->container(get_string('upgradeassignmentsuccess', 'tool_assignmentupgrade'));
-
-            } else {
-                $output .= $this->container(get_string('upgradeassignmentfailed', 'tool_assignmentupgrade', $assignment));
-            }
-            if (isset($assignmentsummary->courseid)) {
-                $output .= html_writer::link(new moodle_url('/course/view.php', array('id'=>$assignmentsummary->courseid)) ,get_string('viewcourse', 'tool_assignmentupgrade'));
-            }
-
-
-        }
-
-        $output .= $this->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
-
-
-        $output .= $this->footer();
-        return $output;
-    }
-
     /**
      * Render the result of an assignment conversion
      * @param stdClass $assignmentsummary data about the assignment to upgrade.
@@ -186,19 +154,17 @@ class tool_assignmentupgrade_renderer extends plugin_renderer_base {
      */
     public function convert_assignment_result($assignmentsummary, $success, $log) {
         $output = '';
-        $output .= $this->header();
-        $output .= $this->heading(get_string('conversioncomplete', 'tool_assignmentupgrade'));
 
+        $output .= $this->container_start('tool_assignmentupgrade_result');
+        $output .= $this->container(get_string('upgradeassignmentsummary', 'tool_assignmentupgrade', $assignmentsummary));
         if (!$success) {
-            $output .= get_string('conversionfailed', 'tool_assignmentupgrade', $log);
+            $output .= $this->container(get_string('conversionfailed', 'tool_assignmentupgrade', $log));
         } else {
-            $output .= html_writer::link(new moodle_url('/course/view.php', array('id'=>$assignmentsummary->courseid)) ,get_string('viewcourse', 'tool_assignmentupgrade'));
+            $output .= $this->container(get_string('upgradeassignmentsuccess', 'tool_assignmentupgrade'));
+            $output .= $this->container(html_writer::link(new moodle_url('/course/view.php', array('id'=>$assignmentsummary->courseid)) ,get_string('viewcourse', 'tool_assignmentupgrade')));
         }
+        $output .= $this->container_end();
 
-        $output .= $this->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
-
-
-        $output .= $this->footer();
         return $output;
     }
 
index 0188dc4..277400b 100644 (file)
@@ -8,4 +8,4 @@
 #page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable tr.selectedrow td { background-color: #ffeecc; }
 #page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable tr.unselectedrow td { background-color: white; }
 
-
+#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_paginationform .hidden { display: none; }
index 0e4830b..307a8c9 100644 (file)
@@ -70,12 +70,11 @@ class tool_assignmentupgrade_assignments_table extends table_sql implements rend
         $from = '{assignment} a JOIN {course} c ON a.course = c.id ' .
                         ' LEFT JOIN {assignment_submissions} s ON a.id = s.assignment';
 
-
         $where = '1 = 1';
         $where .= ' GROUP BY a.id, a.name, a.assignmenttype, c.shortname, c.id ';
 
         $this->set_sql($fields, $from, $where, array());
-        $this->set_count_sql('SELECT COUNT(*) FROM ' . $from, array());
+        $this->set_count_sql('SELECT COUNT(*) FROM {assignment} a JOIN {course} c ON a.course = c.id', array());
 
         $columns = array();
         $headers = array();
index 2464ebe..5125bbb 100644 (file)
@@ -23,8 +23,8 @@
  */
 
 require_once(dirname(__FILE__) . '/../../../config.php');
-require_once(dirname(__FILE__) . '/locallib.php');
 require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/locallib.php');
 
 require_sesskey();
 
@@ -36,13 +36,11 @@ admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrad
 $PAGE->navbar->add(get_string('upgradesingle', 'tool_assignmentupgrade'));
 $renderer = $PAGE->get_renderer('tool_assignmentupgrade');
 
-$assignmentinfo = tool_assignmentupgrade_get_assignment($assignmentid);
-if (!$assignmentinfo) {
-    print_error('invalidrequest');
-    die();
-}
-
 $log = '';
-$result = tool_assignmentupgrade_upgrade_assignment($assignmentinfo, $log);
+list($summary, $success, $log) = tool_assignmentupgrade_upgrade_assignment($assignmentid);
 
-echo $renderer->convert_assignment_result($assignmentinfo, $result, $log);
+echo $renderer->header();
+echo $renderer->heading(get_string('conversioncomplete', 'tool_assignmentupgrade'));
+echo $renderer->convert_assignment_result($summary, $success, $log);
+echo $renderer->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
+echo $renderer->footer();
index 0325b4a..b7ec24c 100644 (file)
@@ -23,8 +23,8 @@
  */
 
 require_once(dirname(__FILE__) . '/../../../config.php');
-require_once(dirname(__FILE__) . '/locallib.php');
 require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/admin/tool/assignmentupgrade/locallib.php');
 
 require_sesskey();
 
index 8d28dbc..77476a9 100644 (file)
@@ -192,6 +192,8 @@ if (!empty($userid)) {
         if (!blog_user_can_view_user_entry($userid)) {
             print_error('cannotviewcourseblog', 'blog');
         }
+
+        $PAGE->navigation->extend_for_user($user);
     }
 }
 
index dcf669e..a16b83a 100644 (file)
@@ -385,6 +385,9 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
             },
             toggle_textarea: function(focus) {
                 var t = Y.one('#dlg-content-'+this.client_id);
+                if (!t) {
+                    return false;
+                }
                 if (focus) {
                     if (t.get('value') == M.str.moodle.addcomment) {
                         t.set('value', '');
index 2bd3ed9..3d4e17b 100644 (file)
@@ -103,44 +103,43 @@ if ($form->is_cancelled()){
 
     // Handle aggregation methods
     // Overall aggregation
-    $aggregation = new completion_aggregation();
-    $aggregation->course = $data->id;
-    $aggregation->criteriatype = null;
+    $aggdata = array(
+        'course'        => $data->id,
+        'criteriatype'  => null
+    );
+    $aggregation = new completion_aggregation($aggdata);
     $aggregation->setMethod($data->overall_aggregation);
-    $aggregation->insert();
+    $aggregation->save();
 
     // Activity aggregation
     if (empty($data->activity_aggregation)) {
         $data->activity_aggregation = 0;
     }
 
-    $aggregation = new completion_aggregation();
-    $aggregation->course = $data->id;
-    $aggregation->criteriatype = COMPLETION_CRITERIA_TYPE_ACTIVITY;
+    $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ACTIVITY;
+    $aggregation = new completion_aggregation($aggdata);
     $aggregation->setMethod($data->activity_aggregation);
-    $aggregation->insert();
+    $aggregation->save();
 
     // Course aggregation
     if (empty($data->course_aggregation)) {
         $data->course_aggregation = 0;
     }
 
-    $aggregation = new completion_aggregation();
-    $aggregation->course = $data->id;
-    $aggregation->criteriatype = COMPLETION_CRITERIA_TYPE_COURSE;
+    $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_COURSE;
+    $aggregation = new completion_aggregation($aggdata);
     $aggregation->setMethod($data->course_aggregation);
-    $aggregation->insert();
+    $aggregation->save();
 
     // Role aggregation
     if (empty($data->role_aggregation)) {
         $data->role_aggregation = 0;
     }
 
-    $aggregation = new completion_aggregation();
-    $aggregation->course = $data->id;
-    $aggregation->criteriatype = COMPLETION_CRITERIA_TYPE_ROLE;
+    $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ROLE;
+    $aggregation = new completion_aggregation($aggdata);
     $aggregation->setMethod($data->role_aggregation);
-    $aggregation->insert();
+    $aggregation->save();
 
     // Update course total passing grade
     if (!empty($data->criteria_grade)) {
@@ -152,7 +151,10 @@ if ($form->is_cancelled()){
         }
     }
 
-    redirect($CFG->wwwroot."/course/view.php?id=$course->id", get_string('changessaved'));
+    add_to_log($course->id, 'course', 'completion updated', 'completion.php?id='.$course->id);
+
+    $url = new moodle_url('/course/view.php', array('id' => $course->id));
+    redirect($url);
 }
 
 
index 01eb3c1..c0ed633 100644 (file)
@@ -557,7 +557,13 @@ class dndupload_ajax_processor {
         }
         // The following are used inside some few core functions, so may as well set them here.
         $this->cm->coursemodule = $this->cm->id;
-        $this->cm->groupmodelink = (!$this->course->groupmodeforce);
+        $groupbuttons = ($this->course->groupmode or (!$this->course->groupmodeforce));
+        if ($groupbuttons and plugin_supports('mod', $this->module->name, FEATURE_GROUPS, 0)) {
+            $this->cm->groupmodelink = (!$this->course->groupmodeforce);
+        } else {
+            $this->cm->groupmodelink = false;
+            $this->cm->groupmode = false;
+        }
     }
 
     /**
@@ -619,6 +625,8 @@ class dndupload_ajax_processor {
             throw new moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
         }
         $mod = $info->cms[$this->cm->id];
+        $mod->groupmodelink = $this->cm->groupmodelink;
+        $mod->groupmode = $this->cm->groupmode;
 
         // Trigger mod_created event with information about this module.
         $eventdata = new stdClass();
@@ -636,12 +644,6 @@ class dndupload_ajax_processor {
                    "view.php?id=$mod->id",
                    "$instanceid", $mod->id);
 
-        if ($this->cm->groupmodelink && plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
-            $mod->groupmodelink = $this->cm->groupmodelink;
-        } else {
-            $mod->groupmodelink = false;
-        }
-
         $this->send_response($mod);
     }
 
index 28aed42..159f94b 100644 (file)
@@ -38,7 +38,7 @@ M.course.format.swap_sections = function(Y, node1, node2) {
         COURSECONTENT : 'course-content',
         LEFT : 'left',
         SECTIONADDMENUS : 'section_add_menus',
-        WEEKDATES: 'weekdates'
+        WEEKDATES: 'sectionname'
     };
 
     var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
index e6f0a22..ac8e582 100644 (file)
@@ -277,7 +277,7 @@ class core_files_renderer extends plugin_renderer_base {
      */
     private function fm_js_template_listfilename() {
         $rv = '
-<span>
+<span class="fp-filename-icon">
     <a href="#">
     <span class="{!}fp-icon"></span>
     <span class="{!}fp-filename"></span>
@@ -535,6 +535,7 @@ class core_files_renderer extends plugin_renderer_base {
                     <a class="{!}fp-vb-details" href="#"></a>
                     <a class="{!}fp-vb-tree" href="#"></a>
                 </div>
+                <div class="fp-clear-right"></div>
             </div>
             <div class="fp-pathbar">
                  <span class="{!}fp-path-folder"><a class="{!}fp-path-folder-name" href="#"></a></span>
@@ -591,7 +592,13 @@ class core_files_renderer extends plugin_renderer_base {
      * @return string
      */
     private function fp_js_template_listfilename() {
-        $rv = '<span><span class="{!}fp-icon"></span> <span class="{!}fp-filename"></span></span>';
+        $rv = '
+<span class="fp-filename-icon">
+    <a href="#">
+        <span class="{!}fp-icon"></span>
+        <span class="{!}fp-filename"></span>
+    </a>
+</span>';
         return preg_replace('/\{\!\}/', '', $rv);
     }
 
index e7a0786..e6e626a 100644 (file)
@@ -49,13 +49,16 @@ class completion_aggregation extends data_object {
      */
     public $required_fields = array('id', 'course', 'criteriatype', 'method', 'value');
 
+    /* @var array Array of unique fields, used in where clauses */
+    public $unique_fields = array('course', 'criteriatype');
+
     /* @var int Course id */
     public $course;
 
     /* @var int Criteria type this aggregation method applies to, or NULL for overall course aggregation */
     public $criteriatype;
 
-    /* @var int Aggregation method (COMPLETION_AGGREGATION_* constant)*/
+    /* @var int Aggregation method (COMPLETION_AGGREGATION_* constant) */
     public $method;
 
     /* @var mixed Method value */
@@ -98,4 +101,19 @@ class completion_aggregation extends data_object {
             $this->method = COMPLETION_AGGREGATION_ALL;
         }
     }
+
+
+    /**
+     * Save aggregation method to database
+     *
+     * @access  public
+     * @return  boolean
+     */
+    public function save() {
+        if ($this->id) {
+            return $this->update();
+        } else {
+            return $this->insert();
+        }
+    }
 }
index 2b5d8bf..e6523ea 100644 (file)
@@ -111,7 +111,7 @@ class completion_completion extends data_object {
             $this->timeenrolled = $timeenrolled;
         }
 
-        $this->_save();
+        return $this->_save();
     }
 
     /**
@@ -137,7 +137,7 @@ class completion_completion extends data_object {
             $this->timestarted = $timestarted;
         }
 
-        $this->_save();
+        return $this->_save();
     }
 
     /**
@@ -165,24 +165,24 @@ class completion_completion extends data_object {
         $this->timecompleted = $timecomplete;
 
         // Save record
-        $this->_save();
+        return $this->_save();
     }
 
     /**
      * Save course completion status
      *
      * This method creates a course_completions record if none exists
+     * @access  private
+     * @return  bool
      */
     private function _save() {
-        global $DB;
-
         if ($this->timeenrolled === null) {
             $this->timeenrolled = 0;
         }
 
         // Save record
         if ($this->id) {
-            $this->update();
+            return $this->update();
         } else {
             // Make sure reaggregate field is not null
             if (!$this->reaggregate) {
@@ -191,10 +191,10 @@ class completion_completion extends data_object {
 
             // Make sure timestarted is not null
             if (!$this->timestarted) {
-                    $this->timestarted = 0;
+                $this->timestarted = 0;
             }
-                       
-            $this->insert();
+
+            return $this->insert();
         }
     }
 }
index d93e9a0..31f5187 100644 (file)
@@ -167,11 +167,11 @@ abstract class completion_criteria extends data_object {
     public static function factory($params) {
         global $CFG, $COMPLETION_CRITERIA_TYPES;
 
-        if (!isset($params->criteriatype) || !isset($COMPLETION_CRITERIA_TYPES[$params->criteriatype])) {
+        if (!isset($params['criteriatype']) || !isset($COMPLETION_CRITERIA_TYPES[$params['criteriatype']])) {
             error('invalidcriteriatype', 'completion');
         }
 
-        $class = 'completion_criteria_'.$COMPLETION_CRITERIA_TYPES[$params->criteriatype];
+        $class = 'completion_criteria_'.$COMPLETION_CRITERIA_TYPES[$params['criteriatype']];
         require_once($CFG->libdir.'/completion/'.$class.'.php');
 
         return new $class($params, false);
index 409bf42..ba27546 100644 (file)
@@ -235,8 +235,7 @@ class completion_criteria_activity extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql);
         foreach ($rs as $record) {
-
-            $completion = new completion_criteria_completion((array)$record);
+            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
             $completion->mark_complete($record->timecompleted);
         }
         $rs->close();
index f4d700d..7f44800 100644 (file)
@@ -44,6 +44,9 @@ class completion_criteria_completion extends data_object {
     /* @var array Array of required table fields, must start with 'id'. */
     public $required_fields = array('id', 'userid', 'course', 'criteriaid', 'gradefinal', 'rpl', 'deleted', 'unenroled', 'timecompleted');
 
+    /* @var array Array of unique fields, used in where clauses */
+    public $unique_fields = array('userid', 'course', 'criteriaid');
+
     /* @var int User ID */
     public $userid;
 
index 882ee63..04be448 100644 (file)
@@ -193,8 +193,8 @@ class completion_criteria_course extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql);
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion((array)$record);
-            $completion->mark_complete($record->timecompleted);
+            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion->mark_complete($record['timecompleted']);
         }
         $rs->close();
     }
index e6e81c0..bacc144 100644 (file)
@@ -179,8 +179,8 @@ class completion_criteria_date extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql, array(time()));
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion((array)$record);
-            $completion->mark_complete($record->timeend);
+            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion->mark_complete($record['timeend']);
         }
         $rs->close();
     }
index 97bcb92..962b052 100644 (file)
@@ -229,8 +229,7 @@ class completion_criteria_duration extends completion_criteria {
         $now = time();
         $rs = $DB->get_recordset_sql($sql, array($now, $now));
         foreach ($rs as $record) {
-
-            $completion = new completion_criteria_completion((array)$record);
+            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
 
             // Use time start if not 0, otherwise use timeenrolled
             if ($record->otimestart) {
index e452454..3e0da11 100644 (file)
@@ -215,8 +215,8 @@ class completion_criteria_grade extends completion_criteria {
         // Loop through completions, and mark as complete
         $rs = $DB->get_recordset_sql($sql);
         foreach ($rs as $record) {
-            $completion = new completion_criteria_completion((array)$record);
-            $completion->mark_complete($record->timecompleted);
+            $completion = new completion_criteria_completion($record, DATA_OBJECT_FETCH_BY_KEY);
+            $completion->mark_complete($record['timecompleted']);
         }
         $rs->close();
     }
index be480d4..3645c06 100644 (file)
@@ -317,7 +317,7 @@ function completion_cron_completions() {
             foreach ($completions as $params) {
                 $timecompleted = max($timecompleted, $params->timecompleted);
 
-                $completion = new completion_criteria_completion($params, false);
+                $completion = new completion_criteria_completion((array)$params, false);
 
                 // Handle aggregation special cases
                 if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
index 01345ee..9d862ba 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+
+/**
+ * Trigger for the new data_object api.
+ *
+ * See data_object::__constructor
+ */
+define('DATA_OBJECT_FETCH_BY_KEY',  2);
+
 /**
  * A data abstraction object that holds methods and attributes
  *
@@ -50,32 +58,74 @@ abstract class data_object {
      */
     public $optional_fields = array();
 
+    /* @var Array of unique fields, used in where clauses and constructor */
+    public $unique_fields = array();
+
     /* @var int The primary key */
     public $id;
 
+
     /**
      * Constructor. Optionally (and by default) attempts to fetch corresponding row from DB.
      *
-     * @param array $params an array with required parameters for this data object.
-     * @param bool $fetch Whether to fetch corresponding row from DB or not,
-     *        optional fields might not be defined if false used
+     * If $fetch is not false, there are a few different things that can happen:
+     * - true:
+     *   load corresponding row from the database, using $params as the WHERE clause
+     *
+     * - DATA_OBJECT_FETCH_BY_KEY:
+     *  load corresponding row from the database, using only the $id in the WHERE clause (if set),
+     *  otherwise using the columns listed in $this->unique_fields.
+     *
+     * - array():
+     *   load corresponding row from the database, using the columns listed in this array
+     *   in the WHERE clause
+     *
+     * @param   array   $params     required parameters and their values for this data object
+     * @param   mixed   $fetch      if false, do not attempt to fetch from the database, otherwise see notes
      */
     public function __construct($params = null, $fetch = true) {
-        if (!empty($params) and (is_array($params) or is_object($params))) {
-            if ($fetch) {
-                if ($data = $this->fetch($params)) {
-                    self::set_properties($this, $data);
-                } else {
-                    self::set_properties($this, $this->optional_fields);//apply defaults for optional fields
-                    self::set_properties($this, $params);
-                }
 
-            } else {
-                self::set_properties($this, $params);
+        if (is_object($params)) {
+            throw new coding_exception('data_object params should be in the form of an array, not an object');
+        }
+
+        // If no params given, apply defaults for optional fields
+        if (empty($params) || !is_array($params)) {
+            self::set_properties($this, $this->optional_fields);
+            return;
+        }
+
+        // If fetch is false, do not load from database
+        if ($fetch === false) {
+            self::set_properties($this, $params);
+            return;
+        }
+
+        // Compose where clause only from fields in unique_fields
+        if ($fetch === DATA_OBJECT_FETCH_BY_KEY && !empty($this->unique_fields)) {
+            if (empty($params['id'])) {
+                $where = array_intersect_key($params, array_flip($this->unique_fields));
+            }
+            else {
+                $where = array('id' => $params['id']);
             }
+        // Compose where clause from given field names
+        } else if (is_array($fetch) && !empty($fetch)) {
+            $where = array_intersect_key($params, array_flip($fetch));
+        // Use entire params array for where clause
+        } else {
+            $where = $params;
+        }
 
+        // Attempt to load from database
+        if ($data = $this->fetch($where)) {
+            // Apply data from database, then data sent to constructor
+            self::set_properties($this, $data);
+            self::set_properties($this, $params);
         } else {
-            self::set_properties($this, $this->optional_fields);//apply defaults for optional fields
+            // Apply defaults for optional fields, then data from constructor
+            self::set_properties($this, $this->optional_fields);
+            self::set_properties($this, $params);
         }
     }
 
@@ -339,4 +389,4 @@ abstract class data_object {
      */
     public function notify_changed($deleted) {
     }
-}
\ No newline at end of file
+}
index a47a1f8..2034af1 100644 (file)
@@ -322,8 +322,9 @@ class completion_info {
      */
     public function get_user_completion($user_id, $criteria) {
         $params = array(
+            'course'        => $this->course_id,
+            'userid'        => $user_id,
             'criteriaid'    => $criteria->id,
-            'userid'        => $user_id
         );
 
         $completion = new completion_criteria_completion($params);
@@ -362,7 +363,7 @@ class completion_info {
             // Build array of criteria objects
             $this->criteria = array();
             foreach ($records as $record) {
-                $this->criteria[$record->id] = completion_criteria::factory($record);
+                $this->criteria[$record->id] = completion_criteria::factory((array)$record);
             }
         }
 
index 0e47a65..f89adbd 100644 (file)
@@ -376,7 +376,7 @@ function cron_run() {
 
     //Run registration updated cron
     mtrace(get_string('siteupdatesstart', 'hub'));
-    require_once($CFG->dirroot . '/admin/registration/lib.php');
+    require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
     $registrationmanager = new registration_manager();
     $registrationmanager->cron();
     mtrace(get_string('siteupdatesend', 'hub'));
index f0bb45d..b3e9aa2 100644 (file)
@@ -319,8 +319,9 @@ function xmldb_main_install() {
     set_role_contextlevels($guestrole,          get_default_contextlevels('guest'));
     set_role_contextlevels($userrole,           get_default_contextlevels('user'));
 
-    // Init themes
-    set_config('themerev', 1);
+    // Init theme and JS revisions
+    set_config('themerev', time());
+    set_config('jsrev', time());
 
     // Install licenses
     require_once($CFG->libdir . '/licenselib.php');
index dbc1133..aa46e62 100644 (file)
@@ -732,8 +732,8 @@ function file_save_draft_area_files($draftitemid, $contextid, $component, $filea
     } else if (count($oldfiles) < 2) {
         $filecount = 0;
         // there were no files before - one file means root dir only ;-)
-        $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
         foreach ($draftfiles as $file) {
+            $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
             if (!$options['subdirs']) {
                 if ($file->get_filepath() !== '/' or $file->is_directory()) {
                     continue;
@@ -766,7 +766,6 @@ function file_save_draft_area_files($draftitemid, $contextid, $component, $filea
     } else {
         // we have to merge old and new files - we want to keep file ids for files that were not changed
         // we change time modified for all new and changed files, we keep time created as is
-        $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
 
         $newhashes = array();
         foreach ($draftfiles as $file) {
@@ -835,6 +834,7 @@ function file_save_draft_area_files($draftitemid, $contextid, $component, $filea
         // Add fresh file or the file which has changed status
         // the size and subdirectory tests are extra safety only, the UI should prevent it
         foreach ($newhashes as $file) {
+            $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
             if (!$options['subdirs']) {
                 if ($file->get_filepath() !== '/' or $file->is_directory()) {
                     continue;
@@ -1367,7 +1367,7 @@ function &get_mimetypes_array() {
         'asc'  => array ('type'=>'text/plain', 'icon'=>'text'),
         'asm'  => array ('type'=>'text/plain', 'icon'=>'text'),
         'au'   => array ('type'=>'audio/au', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
-        'avi'  => array ('type'=>'video/x-ms-wm', 'icon'=>'avi', 'groups'=>array('video'), 'string'=>'video'),
+        'avi'  => array ('type'=>'video/x-ms-wm', 'icon'=>'avi', 'groups'=>array('video','web_video'), 'string'=>'video'),
         'bmp'  => array ('type'=>'image/bmp', 'icon'=>'bmp', 'groups'=>array('image'), 'string'=>'image'),
         'c'    => array ('type'=>'text/plain', 'icon'=>'text'),
         'cct'  => array ('type'=>'shockwave/director', 'icon'=>'flash'),
@@ -1390,8 +1390,8 @@ function &get_mimetypes_array() {
         'dxr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
         'eps'  => array ('type'=>'application/postscript', 'icon'=>'eps'),
         'fdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
-        'flv'  => array ('type'=>'video/x-flv', 'icon'=>'flash', 'groups'=>array('video'), 'string'=>'video'),
-        'f4v'  => array ('type'=>'video/mp4', 'icon'=>'flash', 'groups'=>array('video'), 'string'=>'video'),
+        'flv'  => array ('type'=>'video/x-flv', 'icon'=>'flash', 'groups'=>array('video','web_video'), 'string'=>'video'),
+        'f4v'  => array ('type'=>'video/mp4', 'icon'=>'flash', 'groups'=>array('video','web_video'), 'string'=>'video'),
         'gif'  => array ('type'=>'image/gif', 'icon'=>'gif', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
         'gtar' => array ('type'=>'application/x-gtar', 'icon'=>'zip', 'groups'=>array('archive'), 'string'=>'archive'),
         'tgz'  => array ('type'=>'application/g-zip', 'icon'=>'zip', 'groups'=>array('archive'), 'string'=>'archive'),
@@ -1422,16 +1422,16 @@ function &get_mimetypes_array() {
         'latex'=> array ('type'=>'application/x-latex', 'icon'=>'text'),
         'm'    => array ('type'=>'text/plain', 'icon'=>'text'),
         'mbz'  => array ('type'=>'application/vnd.moodle.backup', 'icon'=>'moodle'),
-        'mov'  => array ('type'=>'video/quicktime', 'icon'=>'mov', 'groups'=>array('video'), 'string'=>'video'),
+        'mov'  => array ('type'=>'video/quicktime', 'icon'=>'mov', 'groups'=>array('video','web_video'), 'string'=>'video'),
         'movie'=> array ('type'=>'video/x-sgi-movie', 'icon'=>'mov', 'groups'=>array('video'), 'string'=>'video'),
         'm3u'  => array ('type'=>'audio/x-mpegurl', 'icon'=>'mp3', 'groups'=>array('audio'), 'string'=>'audio'),
-        'mp3'  => array ('type'=>'audio/mp3', 'icon'=>'mp3', 'groups'=>array('audio'), 'string'=>'audio'),
-        'mp4'  => array ('type'=>'video/mp4', 'icon'=>'mpeg', 'groups'=>array('video'), 'string'=>'video'),
-        'm4v'  => array ('type'=>'video/mp4', 'icon'=>'mpeg', 'groups'=>array('video'), 'string'=>'video'),
+        'mp3'  => array ('type'=>'audio/mp3', 'icon'=>'mp3', 'groups'=>array('audio','web_audio'), 'string'=>'audio'),
+        'mp4'  => array ('type'=>'video/mp4', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
+        'm4v'  => array ('type'=>'video/mp4', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
         'm4a'  => array ('type'=>'audio/mp4', 'icon'=>'mp3', 'groups'=>array('audio'), 'string'=>'audio'),
-        'mpeg' => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video'), 'string'=>'video'),
-        'mpe'  => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video'), 'string'=>'video'),
-        'mpg'  => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video'), 'string'=>'video'),
+        'mpeg' => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
+        'mpe'  => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
+        'mpg'  => array ('type'=>'video/mpeg', 'icon'=>'mpeg', 'groups'=>array('video','web_video'), 'string'=>'video'),
 
         'odt'  => array ('type'=>'application/vnd.oasis.opendocument.text', 'icon'=>'odt', 'groups'=>array('document')),
         'ott'  => array ('type'=>'application/vnd.oasis.opendocument.text-template', 'icon'=>'odt', 'groups'=>array('document')),
@@ -1469,8 +1469,8 @@ function &get_mimetypes_array() {
         'ppsm' => array ('type'=>'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'icon'=>'pptx'),
 
         'ps'   => array ('type'=>'application/postscript', 'icon'=>'pdf'),
-        'qt'   => array ('type'=>'video/quicktime', 'icon'=>'mov', 'groups'=>array('video'), 'string'=>'video'),
-        'ra'   => array ('type'=>'audio/x-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
+        'qt'   => array ('type'=>'video/quicktime', 'icon'=>'mov', 'groups'=>array('video','web_video'), 'string'=>'video'),
+        'ra'   => array ('type'=>'audio/x-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio','web_audio'), 'string'=>'audio'),
         'ram'  => array ('type'=>'audio/x-pn-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
         'rhb'  => array ('type'=>'text/xml', 'icon'=>'xml'),
         'rm'   => array ('type'=>'audio/x-pn-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
@@ -1483,11 +1483,11 @@ function &get_mimetypes_array() {
         'smi'  => array ('type'=>'application/smil', 'icon'=>'text'),
         'smil' => array ('type'=>'application/smil', 'icon'=>'text'),
         'sqt'  => array ('type'=>'text/xml', 'icon'=>'xml'),
-        'svg'  => array ('type'=>'image/svg+xml', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
-        'svgz' => array ('type'=>'image/svg+xml', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
+        'svg'  => array ('type'=>'image/svg+xml', 'icon'=>'image', 'groups'=>array('image','web_image'), 'string'=>'image'),
+        'svgz' => array ('type'=>'image/svg+xml', 'icon'=>'image', 'groups'=>array('image','web_image'), 'string'=>'image'),
         'swa'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
-        'swf'  => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash'),
-        'swfl' => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash'),
+        'swf'  => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash', 'groups'=>array('video','web_video')),
+        'swfl' => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash', 'groups'=>array('video','web_video')),
 
         'sxw'  => array ('type'=>'application/vnd.sun.xml.writer', 'icon'=>'odt'),
         'stw'  => array ('type'=>'application/vnd.sun.xml.writer.template', 'icon'=>'odt'),
@@ -1653,14 +1653,15 @@ function file_file_icon($file, $size = null) {
     } else {
         $mimetype = '';
     }
-    if ($filename && pathinfo($filename, PATHINFO_EXTENSION) &&
-            (!$mimetype || mimeinfo('type', $filename) === $mimetype)) {
-        // files with different extensions sharing the same mimetype (i.e. 'text/plain') may have different icons
-        return file_extension_icon($filename, $size);
-    } else {
-        // if mimetype and extension do not match we assume that mimetype is more correct
-        return file_mimetype_icon($mimetype, $size);
+    $mimetypes = &get_mimetypes_array();
+    if ($filename) {
+        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+        if ($extension && !empty($mimetypes[$extension])) {
+            // if file name has known extension, return icon for this extension
+            return file_extension_icon($filename, $size);
+        }
     }
+    return file_mimetype_icon($mimetype, $size);
 }
 
 /**
@@ -1682,13 +1683,18 @@ function file_file_icon($file, $size = null) {
 function file_folder_icon($iconsize = null) {
     global $CFG;
     static $iconpostfixes = array(256=>'-256', 128=>'-128', 96=>'-96', 80=>'-80', 72=>'-72', 64=>'-64', 48=>'-48', 32=>'-32', 24=>'-24', 16=>'');
+    static $cached = array();
     $iconsize = max(array(16, (int)$iconsize));
-    foreach ($iconpostfixes as $size => $postfix) {
-        $fullname = $CFG->dirroot.'/pix/f/folder'.$postfix;
-        if ($iconsize >= $size && (file_exists($fullname.'.png') || file_exists($fullname.'.gif'))) {
-            return 'f/folder'.$postfix;
+    if (!array_key_exists($iconsize, $cached)) {
+        foreach ($iconpostfixes as $size => $postfix) {
+            $fullname = $CFG->dirroot.'/pix/f/folder'.$postfix;
+            if ($iconsize >= $size && (file_exists($fullname.'.png') || file_exists($fullname.'.gif'))) {
+                $cached[$iconsize] = 'f/folder'.$postfix;
+                break;
+            }
         }
     }
+    return $cached[$iconsize];
 }
 
 /**
@@ -1749,6 +1755,7 @@ function file_extension_icon($filename, $size = NULL) {
  * @return string Text description
  */
 function get_mimetype_description($obj, $capitalise=false) {
+    $filename = $mimetype = '';
     if (is_object($obj) && method_exists($obj, 'get_filename') && method_exists($obj, 'get_mimetype')) {
         // this is an instance of stored_file
         $mimetype = $obj->get_mimetype();
@@ -1761,22 +1768,22 @@ function get_mimetype_description($obj, $capitalise=false) {
         $obj = (array)$obj;
         if (!empty($obj['filename'])) {
             $filename = $obj['filename'];
-        } else {
-            $filename = '';
         }
         if (!empty($obj['mimetype'])) {
             $mimetype = $obj['mimetype'];
-        } else {
-            $mimetype = mimeinfo('type', $filename);
         }
     } else {
         $mimetype = $obj;
-        $filename = '';
+    }
+    $mimetypefromext = mimeinfo('type', $filename);
+    if (empty($mimetype) || $mimetypefromext !== 'document/unknown') {
+        // if file has a known extension, overwrite the specified mimetype
+        $mimetype = $mimetypefromext;
     }
     $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
     if (empty($extension)) {
         $mimetypestr = mimeinfo_from_type('string', $mimetype);
-        $extension = mimeinfo_from_type('extension', $mimetype);
+        $extension = str_replace('.', '', mimeinfo_from_type('extension', $mimetype));
     } else {
         $mimetypestr = mimeinfo('string', $filename);
     }
@@ -1835,7 +1842,7 @@ function file_get_typegroup($element, $groups) {
                 if (empty($value[$element])) {
                     continue;
                 }
-                if (($group === $extension || $group === $value['type'] ||
+                if (($group === '.'.$extension || $group === $value['type'] ||
                         (!empty($value['groups']) && in_array($group, $value['groups']))) &&
                         !in_array($value[$element], $cached[$element][$group])) {
                     $cached[$element][$group][] = $value[$element];
@@ -4080,6 +4087,12 @@ function file_pluginfile($relativepath, $forcedownload, $preview = null) {
                 // somebody tries to gain illegal access, cm type must match the component!
                 send_file_not_found();
             }
+
+            $bprecord = $DB->get_record('block_positions', array('blockinstanceid' => $context->instanceid), 'visible');
+            // User can't access file, if block is hidden or doesn't have block:view capability
+            if (($bprecord && !$bprecord->visible) || !has_capability('moodle/block:view', $context)) {
+                 send_file_not_found();
+            }
         } else {
             $birecord = null;
         }
index 149936b..edac5a5 100644 (file)
@@ -272,7 +272,7 @@ class file_storage {
     public function get_file_by_id($fileid) {
         global $DB;
 
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
@@ -293,7 +293,7 @@ class file_storage {
     public function get_file_by_hash($pathnamehash) {
         global $DB;
 
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
@@ -370,7 +370,7 @@ class file_storage {
      */
     public function get_external_files($repositoryid, $sort = 'sortorder, itemid, filepath, filename') {
         global $DB;
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
@@ -407,7 +407,7 @@ class file_storage {
             $itemidsql = '';
         }
 
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
@@ -504,7 +504,7 @@ class file_storage {
             $dirs = $includedirs ? "" : "AND filename <> '.'";
             $length = textlib::strlen($filepath);
 
-            $sql = "SELECT f.*, r.repositoryid, r.reference
+            $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                       FROM {files} f
                  LEFT JOIN {files_reference} r
                            ON f.referencefileid = r.id
@@ -534,7 +534,7 @@ class file_storage {
             $length = textlib::strlen($filepath);
 
             if ($includedirs) {
-                $sql = "SELECT f.*, r.repositoryid, r.reference
+                $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                           FROM {files} f
                      LEFT JOIN {files_reference} r
                                ON f.referencefileid = r.id
@@ -553,7 +553,7 @@ class file_storage {
                 }
             }
 
-            $sql = "SELECT f.*, r.repositoryid, r.reference
+            $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                       FROM {files} f
                  LEFT JOIN {files_reference} r
                            ON f.referencefileid = r.id
@@ -772,7 +772,7 @@ class file_storage {
         unset($filerecord['contenthash']);
         unset($filerecord['pathnamehash']);
 
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
@@ -1669,11 +1669,12 @@ class file_storage {
      */
     public function search_references($str) {
         global $DB;
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
-                 WHERE r.reference = ? AND (f.component <> ? OR f.filearea <> ?)";
+                 WHERE ".$DB->sql_compare_text('r.reference').' = '.$DB->sql_compare_text('?')."
+                 AND (f.component <> ? OR f.filearea <> ?)";
 
         $rs = $DB->get_recordset_sql($sql, array($str, 'user', 'draft'));
         $files = array();
@@ -1699,7 +1700,8 @@ class file_storage {
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
-                 WHERE r.reference = ? AND (f.component <> ? OR f.filearea <> ?)";
+                 WHERE ".$DB->sql_compare_text('r.reference').' = '.$DB->sql_compare_text('?')."
+                 AND (f.component <> ? OR f.filearea <> ?)";
 
         $count = $DB->count_records_sql($sql, array($str, 'user', 'draft'));
         return $count;
@@ -1726,11 +1728,12 @@ class file_storage {
 
         $reference = self::pack_reference($params);
 
-        $sql = "SELECT f.*, r.repositoryid, r.reference
+        $sql = "SELECT ".self::instance_sql_fields('f', 'r')."
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
-                 WHERE r.reference = ? AND (f.component <> ? OR f.filearea <> ?)";
+                 WHERE ".$DB->sql_compare_text('r.reference').' = '.$DB->sql_compare_text('?')."
+                   AND (f.component <> ? OR f.filearea <> ?)";
 
         $rs = $DB->get_recordset_sql($sql, array($reference, 'user', 'draft'));
         $files = array();
@@ -1769,7 +1772,8 @@ class file_storage {
                   FROM {files} f
              LEFT JOIN {files_reference} r
                        ON f.referencefileid = r.id
-                 WHERE r.reference = ? AND (f.component <> ? OR f.filearea <> ?)";
+                 WHERE ".$DB->sql_compare_text('r.reference').' = '.$DB->sql_compare_text('?')."
+                 AND (f.component <> ? OR f.filearea <> ?)";
 
         $count = $DB->count_records_sql($sql, array($reference, 'user', 'draft'));
         return $count;
@@ -1794,19 +1798,19 @@ class file_storage {
     /**
      * Return mimetype by given file pathname
      *
-     * This method uses fileinfo module to get mimetype using magic bytes if file exists.
-     * If not, it will get mimetype based on filename
+     * If file has a known extension, we return the mimetype based on extension.
+     * Otherwise (when possible) we try to get the mimetype from file contents.
      *
      * @param string $pathname
      * @return string
      */
     public static function mimetype($pathname) {
-        if (file_exists($pathname)) {
+        $type = mimeinfo('type', $pathname);
+        if ($type === 'document/unknown' && class_exists('finfo') && file_exists($pathname)) {
             $finfo = new finfo(FILEINFO_MIME_TYPE);
-            return mimeinfo_from_type('type', $finfo->file($pathname));
-        } else {
-            return mimeinfo('type', $pathname);
+            $type = mimeinfo_from_type('type', $finfo->file($pathname));
         }
+        return $type;
     }
 
     /**
@@ -1875,5 +1879,37 @@ class file_storage {
             mtrace('done.');
         }
     }
+
+    /**
+     * Get the sql formated fields for a file instance to be created from a
+     * {files} and {files_refernece} join.
+     *
+     * @param string $filesprefix the table prefix for the {files} table
+     * @param string $filesreferenceprefix the table prefix for the {files_reference} table
+     * @return string the sql to go after a SELECT
+     */
+    private static function instance_sql_fields($filesprefix, $filesreferenceprefix) {
+        // Note, these fieldnames MUST NOT overlap between the two tables,
+        // else problems like MDL-33172 occur.
+        $filefields = array('contenthash', 'pathnamehash', 'contextid', 'component', 'filearea',
+            'itemid', 'filepath', 'filename', 'userid', 'filesize', 'mimetype', 'status', 'source',
+            'author', 'license', 'timecreated', 'timemodified', 'sortorder', 'referencefileid',
+            'referencelastsync', 'referencelifetime');
+
+        $referencefields = array('repositoryid', 'reference');
+
+        // id is specifically named to prevent overlaping between the two tables.
+        $fields = array();
+        $fields[] = $filesprefix.'.id AS id';
+        foreach ($filefields as $field) {
+            $fields[] = "{$filesprefix}.{$field}";
+        }
+
+        foreach ($referencefields as $field) {
+            $fields[] = "{$filesreferenceprefix}.{$field}";
+        }
+
+        return implode(', ', $fields);
+    }
 }
 
index 24529f5..d571beb 100644 (file)
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
 
 global $CFG;
 require_once($CFG->libdir . '/filelib.php');
+require_once($CFG->dirroot . '/repository/lib.php');
 
 class filestoragelib_testcase extends advanced_testcase {
 
@@ -207,4 +208,193 @@ class filestoragelib_testcase extends advanced_testcase {
         // still readable?
         $this->assertEquals($content, $importedfile->get_content());
     }
+
+    /**
+     * TODO: the tests following this line were added to demonstrate specific Oracle problems in
+     * MDL-33172. They need to be improved to properly evalulate the results of the tests. This is
+     * tracked in MDL-33326.
+     */
+    private function setup_three_private_files() {
+        global $USER, $DB;
+
+        $this->resetAfterTest(true);
+
+        $generator = $this->getDataGenerator();
+        $user = $generator->create_user();
+        $usercontext = context_user::instance($user->id);
+        $USER = $DB->get_record('user', array('id'=>$user->id));
+        // create a user private file
+        $file1 = new stdClass;
+        $file1->contextid = $usercontext->id;
+        $file1->component = 'user';
+        $file1->filearea  = 'private';
+        $file1->itemid    = 0;
+        $file1->filepath  = '/';
+        $file1->filename  = '1.txt';
+        $file1->source    = 'test';
+
+        $fs = get_file_storage();
+        $userfile1 = $fs->create_file_from_string($file1, 'file1 content');
+        $file2 = clone($file1);
+        $file2->filename = '2.txt';
+        $userfile2 = $fs->create_file_from_string($file2, 'file2 content');
+
+        $file3 = clone($file1);
+        $file3->filename = '3.txt';
+        $userfile3 = $fs->create_file_from_storedfile($file3, $userfile2);
+
+        $user->ctxid = $usercontext->id;
+
+        return $user;
+    }
+
+
+    public function test_get_area_files() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        // Get area files with default options.
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Should be the two files we added plus the folder.
+        $this->assertEquals(4, count($areafiles));
+
+        // Get area files without a folder.
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
+        // Should be the two files without folder.
+        $this->assertEquals(3, count($areafiles));
+
+        // Get area files ordered by id (was breaking on oracle).
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
+        // Should be the two files without folder.
+        $this->assertEquals(3, count($areafiles));
+
+        // Test with an itemid with no files
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', 666, 'sortorder', false);
+        // Should none
+        $this->assertEquals(0, count($areafiles));
+    }
+
+    public function test_get_area_tree() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        // Get area files with default options.
+        $areafiles = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
+        $areafiles = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
+        //TODO: verify result!! MDL-33326
+    }
+
+    public function test_get_file_by_id() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+
+        // Test get_file_by_id.
+        $filebyid = reset($areafiles);
+        $shouldbesame = $fs->get_file_by_id($filebyid->get_id());
+        $this->assertEquals($filebyid->get_contenthash(), $shouldbesame->get_contenthash());
+    }
+
+    public function test_get_file_by_hash() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Test get_file_by_hash
+        $filebyhash = reset($areafiles);
+        $shouldbesame = $fs->get_file_by_hash($filebyhash->get_pathnamehash());
+        $this->assertEquals($filebyhash->get_id(), $shouldbesame->get_id());
+    }
+
+    public function test_get_references_by_storedfile() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        //Test get_file_by_hash
+
+        $testfile = reset($areafiles);
+        $references = $fs->get_references_by_storedfile($testfile);
+        //TODO: verify result!! MDL-33326
+    }
+
+    public function test_get_external_files() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        $repos = repository::get_instances(array('type'=>'user'));
+        $userrepository = reset($repos);
+        $this->assertInstanceOf('repository', $userrepository);
+
+        // this should break on oracle
+        $fs->get_external_files($userrepository->id, 'id');
+        //TODO: verify result!! MDL-33326
+     }
+
+    public function test_get_directory_files() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        // This should also break on oracle.
+        $fs->create_directory($user->ctxid, 'user', 'private', 0, '/');
+        //TODO: verify result!! MDL-33326
+
+        // Don't recurse with dirs
+        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
+        //TODO: verify result!! MDL-33326
+
+        // Don't recurse without dirs
+        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
+        //TODO: verify result!! MDL-33326
+
+        // Recurse with dirs
+        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
+        //TODO: verify result!! MDL-33326
+        // Recurse without dirs
+        $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
+        //TODO: verify result!! MDL-33326
+    }
+
+    public function test_search_references() {
+        $fs = get_file_storage();
+        $references = $fs->search_references('testsearch');
+        //TODO: verify result!! MDL-33326
+    }
+
+    public function test_search_references_count() {
+        $fs = get_file_storage();
+        $references = $fs->search_references_count('testsearch');
+        //TODO: verify result!! MDL-33326
+    }
+
+    public function test_delete_area_files() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        // Get area files with default options.
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Should be the two files we added plus the folder.
+        $this->assertEquals(4, count($areafiles));
+        $fs->delete_area_files($user->ctxid, 'user', 'private');
+
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Should be the two files we added plus the folder.
+        $this->assertEquals(0, count($areafiles));
+    }
+
+    public function test_delete_area_files_select() {
+        $user = $this->setup_three_private_files();
+        $fs = get_file_storage();
+
+        // Get area files with default options.
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Should be the two files we added plus the folder.
+        $this->assertEquals(4, count($areafiles));
+        $fs->delete_area_files_select($user->ctxid, 'user', 'private', '!= :notitemid', array('notitemid'=>9999));
+
+        $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
+        // Should be the two files we added plus the folder.
+        $this->assertEquals(0, count($areafiles));
+    }
 }
index fdbbaf9..f36580b 100644 (file)
@@ -302,7 +302,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
 
             $args = new stdClass();
             // need these three to filter repositories list
-            $args->accepted_types = array('image');
+            $args->accepted_types = array('web_image');
             $args->return_types = (FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE);
             $args->context = $ctx;
             $args->env = 'filepicker';
index 09071fb..c3fad28 100644 (file)
@@ -60,7 +60,7 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input {
      * @param array $options set of options to initalize filepicker
      */
     function MoodleQuickForm_filepicker($elementName=null, $elementLabel=null, $attributes=null, $options=null) {
-        global $CFG;
+        global $CFG, $PAGE;
 
         $options = (array)$options;
         foreach ($options as $name=>$value) {
@@ -68,12 +68,18 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input {
                 $this->_options[$name] = $value;
             }
         }
-        if (!empty($options['return_types'])) {
-            $this->_options['return_types'] = FILE_INTERNAL | FILE_REFERENCE;
+        if (empty($options['return_types'])) {
+            $this->_options['return_types'] = FILE_INTERNAL;
         }
+        $fpmaxbytes = 0;
         if (!empty($options['maxbytes'])) {
-            $this->_options['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $options['maxbytes']);
+            $fpmaxbytes = $options['maxbytes'];
         }
+        $coursemaxbytes = 0;
+        if (!empty($PAGE->course)) {
+            $coursemaxbytes = $PAGE->course->maxbytes;
+        }
+        $this->_options['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $coursemaxbytes, $fpmaxbytes);
         $this->_type = 'filepicker';
         parent::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
     }
@@ -143,7 +149,7 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input {
         $args = new stdClass();
         // need these three to filter repositories list
         $args->accepted_types = $this->_options['accepted_types']?$this->_options['accepted_types']:'*';
-        $args->return_types = FILE_INTERNAL | FILE_REFERENCE;
+        $args->return_types = $this->_options['return_types'];
         $args->itemid = $draftitemid;
         $args->maxbytes = $this->_options['maxbytes'];
         $args->context = $PAGE->context;
index 7543eb5..3766051 100644 (file)
@@ -114,4 +114,4 @@ YUI.add('moodle-form-checkboxcontroller', function(Y) {
     M.form.checkboxcontroller = function(params) {
         return new checkboxcontroller(params);
     }
-}, '@VERSION@', {requires:['base', 'node', 'util']});
+}, '@VERSION@', {requires:['base', 'node']});
index 7cd7abe..dfe8796 100644 (file)
@@ -37,16 +37,24 @@ require_once($CFG->libdir.'/outputrequirementslib.php');
 /**
  * Invalidate all server and client side caches.
  *
- * This method deletes the phsyical directory that is used to cache the theme
+ * This method deletes the physical directory that is used to cache the theme
  * files used for serving.
- * Because it deletes the main theme cache directoy all themes are reset by
+ * Because it deletes the main theme cache directory all themes are reset by
  * this function.
  */
 function theme_reset_all_caches() {
     global $CFG;
     require_once("$CFG->libdir/filelib.php");
 
-    set_config('themerev', empty($CFG->themerev) ? 1 : $CFG->themerev+1);
+    $next = time();
+    if (isset($CFG->themerev) and $next <= $CFG->themerev and $CFG->themerev - $next < 60*60) {
+        // This resolves problems when reset is requested repeatedly within 1s,
+        // the < 1h condition prevents accidental switching to future dates
+        // because we might not recover from it.
+        $next = $CFG->themerev+1;
+    }
+
+    set_config('themerev', $next); // time is unique even when you reset/switch database
     fulldelete("$CFG->cachedir/theme");
 }
 
index 09bd6d6..80f20c6 100644 (file)
@@ -2055,8 +2055,10 @@ EOD;
         if ($options->env != 'url') {
             $html .= <<<EOD
     <div id="file_info_{$client_id}" class="mdl-left filepicker-filelist" style="position: relative">
-    <div><span class="filepicker-filename">$currentfile</span><span class="dndupload-message"> - $strdndenabled </span></div>
-    <div><div class="dndupload-target">{$strdroptoupload}</div></div>
+    <div class="filepicker-filename">
+        <div class="filepicker-container">$currentfile<span class="dndupload-message"> - $strdndenabled <br/><span class="dndupload-arrow"></span></span></div>
+    </div>
+    <div><div class="dndupload-target">{$strdroptoupload}<br/><span class="dndupload-arrow"></span></div></div>
     </div>
 EOD;
         }
index bdf3b81..aa8755e 100644 (file)
@@ -1264,6 +1264,14 @@ function js_reset_all_caches() {
     global $CFG;
     require_once("$CFG->libdir/filelib.php");
 
-    set_config('jsrev', empty($CFG->jsrev) ? 1 : $CFG->jsrev+1);
+    $next = time();
+    if (isset($CFG->jsrev) and $next <= $CFG->jsrev and $CFG->jsrev - $next < 60*60) {
+        // This resolves problems when reset is requested repeatedly within 1s,
+        // the < 1h condition prevents accidental switching to future dates
+        // because we might not recover from it.
+        $next = $CFG->jsrev+1;
+    }
+
+    set_config('jsrev', $next);
     fulldelete("$CFG->cachedir/js");
 }
index 0dc33d0..df5239c 100644 (file)
@@ -1112,12 +1112,12 @@ class available_update_checker {
      */
     protected function cron_execute() {
 
-        $this->restore_response();
-        $previous = $this->recentresponse;
-        $this->fetch();
-        $this->restore_response(true);
-        $current = $this->recentresponse;
         try {
+            $this->restore_response();
+            $previous = $this->recentresponse;
+            $this->fetch();
+            $this->restore_response(true);
+            $current = $this->recentresponse;
             $changes = $this->compare_responses($previous, $current);
             $notifications = $this->cron_notifications($changes);
             $this->cron_notify($notifications);
index 36207ad..53bc317 100644 (file)
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
 
 global $CFG;
 require_once($CFG->libdir . '/filelib.php');
+require_once($CFG->dirroot . '/repository/lib.php');
 
 class filelib_testcase extends advanced_testcase {
     public function test_format_postdata_for_curlcall() {
index 6ddee76..fa97208 100644 (file)
@@ -51,6 +51,7 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
                                                   'preventlatesubmissions',
                                                   'submissiondrafts',
                                                   'sendnotifications',
+                                                  'sendlatenotifications',
                                                   'duedate',
                                                   'allowsubmissionsfromdate',
                                                   'grade',
index 273486b..7974516 100644 (file)
@@ -15,8 +15,9 @@
         <FIELD NAME="nosubmissions" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="This field is a cache for is_any_submission_plugin_enabled() which allows Moodle pages to distinguish offline assignment types without loading the assignment class." PREVIOUS="alwaysshowdescription" NEXT="preventlatesubmissions"/>
         <FIELD NAME="preventlatesubmissions" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If true, submissions will not be accepted past the due date." PREVIOUS="nosubmissions" NEXT="submissiondrafts"/>
         <FIELD NAME="submissiondrafts" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If true, assignment submissions will be considered drafts until the student clicks on the submit assignmnet button." PREVIOUS="preventlatesubmissions" NEXT="sendnotifications"/>
-        <FIELD NAME="sendnotifications" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Allows the disabling of email notifications in the assign module." PREVIOUS="submissiondrafts" NEXT="duedate"/>
-        <FIELD NAME="duedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The due date for the assignment. Displayed to students." PREVIOUS="sendnotifications" NEXT="allowsubmissionsfromdate"/>
+        <FIELD NAME="sendnotifications" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Allows the disabling of email notifications in the assign module." PREVIOUS="submissiondrafts" NEXT="sendlatenotifications"/>
+        <FIELD NAME="sendlatenotifications" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Allows separate enabling of notifications for late assignment submissions." PREVIOUS="sendnotifications" NEXT="duedate"/>
+        <FIELD NAME="duedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The due date for the assignment. Displayed to students." PREVIOUS="sendlatenotifications" NEXT="allowsubmissionsfromdate"/>
         <FIELD NAME="allowsubmissionsfromdate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If set, submissions will only be accepted after this date." PREVIOUS="duedate" NEXT="grade"/>
         <FIELD NAME="grade" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The maximum grade for this assignment. Can be negative to indicate the use of a scale." PREVIOUS="allowsubmissionsfromdate" NEXT="timemodified"/>
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time the settings for this assign module instance were last modified." PREVIOUS="grade"/>
index 1df087f..77581d7 100644 (file)
 
 $messageproviders = array (
 
-// Ordinary assignment submissions
-    'assign_updates' => array (
+    // Ordinary assignment submissions
+    'assign_student_notification' => array (
+        'capability' => 'mod/assign:submit'
+    ),
+    'assign_grader_notification' => array (
+        'capability' => 'mod/assign:grade'
     )
 
 );
index 03ee8f4..e918367 100644 (file)
  * @return bool
  */
 function xmldb_assign_upgrade($oldversion) {
+    global $CFG, $DB;
+
+    $dbman = $DB->get_manager();
+
+    if ($oldversion < 2012051700) {
+
+        // Define field sendlatenotifications to be added to assign
+        $table = new xmldb_table('assign');
+        $field = new xmldb_field('sendlatenotifications', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'sendnotifications');
+
+        // Conditionally launch add field sendlatenotifications
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Assign savepoint reached.
+        upgrade_mod_savepoint(true, 2012051700, 'assign');
+    }
+
     return true;
 }
 
index ace9fab..0daa8ca 100644 (file)
@@ -90,23 +90,34 @@ $string['duedatereached'] = 'The due date for this assignment has now passed';
 $string['duedatevalidation'] = 'Due date must be after the allow submissions from date.';
 $string['editsubmission'] = 'Edit my submission';
 $string['editaction'] = 'Actions...';
-$string['emailgradermail'] = '{$a->username} has updated their assignment submission
+$string['gradersubmissionupdatedtext'] = '{$a->username} has updated their assignment submission
 for \'{$a->assignment}\' at {$a->timeupdated}
 
 It is available here:
 
     {$a->url}';
-$string['emailgradermailhtml'] = '{$a->username} has updated their assignment submission
+$string['gradersubmissionupdatedhtml'] = '{$a->username} has updated their assignment submission
 for <i>\'{$a->assignment}\'  at {$a->timeupdated}</i><br /><br />
 It is <a href="{$a->url}">available on the web site</a>.';
+$string['gradersubmissionupdatedsmall'] = '{$a->username} has updated their submission for assignment {$a->assignment}.';
 $string['enabled'] = 'Enabled';
 $string['errornosubmissions'] = 'There are no submissions to download';
 $string['errorquickgradingvsadvancedgrading'] = 'The grades were not saved because this assignment is currently using advanced grading';
 $string['errorrecordmodified'] = 'The grades were not saved because someone has modified one or more records more recently than when you loaded the page.';
-$string['feedbackcomments'] = 'Feedback comments';
 $string['feedback'] = 'Feedback';
+$string['feedbackavailabletext'] = '{$a->username} has posted some feedback on your
+assignment submission for \'{$a->assignment}\'
+
+You can see it appended to your assignment submission:
+
+    {$a->url}';
+$string['feedbackavailablehtml'] = '{$a->username} has posted some feedback on your
+assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
+You can see it appended to your <a href="{$a->url}">assignment submission</a>.';
+$string['feedbackavailablesmall'] = '{$a->username} has given feedback for assignment {$a->assignment}';
 $string['feedbackplugins'] = 'Feedback plugins';
 $string['feedbackpluginforgradebook'] = 'Feedback plugin that will push comments to the gradebook';
+$string['feedbackpluginforgradebook_help'] = 'Only one assignment feedback plugin can push feedback into the gradebook.';
 $string['feedbackplugin'] = 'Feedback plugin';
 $string['feedbacksettings'] = 'Feedback settings';
 $string['filesubmissions'] = 'File submissions';
@@ -139,7 +150,8 @@ $string['locksubmissionforstudent'] = 'Prevent any more submissions for student:
 $string['locksubmissions'] = 'Lock submissions';
 $string['manageassignfeedbackplugins'] = 'Manage assignment feedback plugins';
 $string['manageassignsubmissionplugins'] = 'Manage assignment submission plugins';
-$string['messageprovider:assign_updates'] = 'Assignment notifications';
+$string['messageprovider:assign_student_notification'] = 'Assignment student notifications';
+$string['messageprovider:assign_grader_notification'] = 'Assignment grader notifications';
 $string['modulename'] = 'Assignment';
 $string['modulename_help'] = 'The assignment activity module enables a teacher to assess students’ learning by setting work and then reviewing it and providing feedback and grades.
 
@@ -186,9 +198,13 @@ $string['reviewed'] = 'Reviewed';
 $string['savechanges'] = 'Save changes';
 $string['saveallchanges'] = 'Save all changes';
 $string['savenext'] = 'Save and show next';
-$string['sendnotifications'] = 'Send notifications to graders';
+$string['sendnotifications'] = 'Notify graders about submissions';
 $string['sendnotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment, early, on time and late. Message methods are configurable.';
 $string['selectlink'] = 'Select...';
+$string['sendlatenotifications'] = 'Notify graders about late submissions';
+$string['sendlatenotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment late. Message methods are configurable.';
+$string['sendsubmissionreceipts'] = 'Send submission receipt to students';
+$string['sendsubmissionreceipts_help'] = 'This switch will enable submission receipts for students. Students will receive a notification every time they successfully submit an assignment';
 $string['settings'] = 'Assignment settings';
 $string['showrecentsubmissions'] = 'Show recent submissions';
 $string['submissiondrafts'] = 'Require students click submit button';
@@ -196,6 +212,16 @@ $string['submissiondrafts_help'] = 'If enabled, students will have to click a Su
 $string['submissionnotready'] = 'This assignment is not ready to submit:';
 $string['submissionplugins'] = 'Submission plugins';
 $string['submissionreceipts'] = 'Send submission receipts';
+$string['submissionreceipttext'] = 'You have submitted an
+assignment submission for \'{$a->assignment}\'
+
+You can see the status of your assignment submission:
+
+    {$a->url}';
+$string['submissionreceipthtml'] = 'You have submitted an
+assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
+You can the status of your <a href="{$a->url}">assignment submission</a>.';
+$string['submissionreceiptsmall'] = 'You have submitted your assignment submission for {$a->assignment}';
 $string['submissionslocked'] = 'This assignment is not accepting submissions';
 $string['submissionslockedshort'] = 'Submission changes not allowed';
 $string['submissions'] = 'Submissions';
@@ -235,4 +261,4 @@ $string['viewownsubmissionform'] = 'View own submit assignment page.';
 $string['viewownsubmissionstatus'] = 'View own submission status page.';
 $string['viewsubmissionforuser'] = 'View submission for user: {$a}';
 $string['viewsubmission'] = 'View submission';
-$string['viewsubmissiongradingtable'] = 'View submission grading table.';
+$string['viewsubmissiongradingtable'] = 'View submission grading table.';
\ No newline at end of file
index 61c9773..b9e71a1 100644 (file)
@@ -674,7 +674,8 @@ function assign_cron() {
     global $CFG;
 
     require_once($CFG->dirroot . '/mod/assign/locallib.php');
-    //assignment::cron();
+    assign::cron();
+
     $plugins = get_plugin_list('assignsubmission');
 
     foreach ($plugins as $name => $plugin) {
index 0e55f45..ff37319 100644 (file)
@@ -419,6 +419,7 @@ class assign {
         $update->preventlatesubmissions = $formdata->preventlatesubmissions;
         $update->submissiondrafts = $formdata->submissiondrafts;
         $update->sendnotifications = $formdata->sendnotifications;
+        $update->sendlatenotifications = $formdata->sendlatenotifications;
         $update->duedate = $formdata->duedate;
         $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
         $update->grade = $formdata->grade;
@@ -636,6 +637,7 @@ class assign {
         $update->preventlatesubmissions = $formdata->preventlatesubmissions;
         $update->submissiondrafts = $formdata->submissiondrafts;
         $update->sendnotifications = $formdata->sendnotifications;
+        $update->sendlatenotifications = $formdata->sendlatenotifications;
         $update->duedate = $formdata->duedate;
         $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
         $update->grade = $formdata->grade;
@@ -1068,16 +1070,139 @@ class assign {
          return false;
     }
 
-
     /**
-     *  Cron function to be run periodically according to the moodle cron
-     *  Finds all assignment notifications that have yet to be mailed out, and mails them
+     * Finds all assignment notifications that have yet to be mailed out, and mails them.
+     *
+     * Cron function to be run periodically according to the moodle cron
      *
      * @return bool
      */
     static function cron() {
-        global $CFG, $USER, $DB;
+        global $DB;
+
+        // only ever send a max of one days worth of updates
+        $yesterday = time() - (24 * 3600);
+        $timenow   = time();
+
+        // Collect all submissions from the past 24 hours that require mailing.
+        $sql = "SELECT s.*, a.course, a.name, g.*, g.id as gradeid, g.timemodified as lastmodified
+                 FROM {assign} a
+                 JOIN {assign_grades} g ON g.assignment = a.id
+            LEFT JOIN {assign_submission} s ON s.assignment = a.id AND s.userid = g.userid
+                WHERE g.timemodified >= :yesterday AND
+                      g.timemodified <= :today AND
+                      g.mailed = 0";
+        $params = array('yesterday' => $yesterday, 'today' => $timenow);
+        $submissions = $DB->get_records_sql($sql, $params);
+
+        if (empty($submissions)) {
+            mtrace('done.');
+            return true;
+        }
+
+        mtrace('Processing ' . count($submissions) . ' assignment submissions ...');
+
+        // Preload courses we are going to need those.
+        $courseids = array();
+        foreach ($submissions as $submission) {
+            $courseids[] = $submission->course;
+        }
+        // Filter out duplicates
+        $courseids = array_unique($courseids);
+        $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
+        list($courseidsql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
+        $sql = "SELECT c.*, {$ctxselect}
+                  FROM {course} c
+             LEFT JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel
+                 WHERE c.id {$courseidsql}";
+        $params['contextlevel'] = CONTEXT_COURSE;
+        $courses = $DB->get_records_sql($sql, $params);
+        // Clean up... this could go on for a while.
+        unset($courseids);
+        unset($ctxselect);
+        unset($courseidsql);
+        unset($params);
+
+        // Simple array we'll use for caching modules.
+        $modcache = array();
+
+        // Email students about new feedback
+        foreach ($submissions as $submission) {
+
+            mtrace("Processing assignment submission $submission->id ...");
+
+            // do not cache user lookups - could be too many
+            if (!$user = $DB->get_record("user", array("id"=>$submission->userid))) {
+                mtrace("Could not find user $submission->userid");
+                continue;
+            }
+
+            // use a cache to prevent the same DB queries happening over and over
+            if (!array_key_exists($submission->course, $courses)) {
+                mtrace("Could not find course $submission->course");
+                continue;
+            }
+            $course = $courses[$submission->course];
+            if (isset($course->ctxid)) {
+                // Context has not yet been preloaded. Do so now.
+                context_helper::preload_from_record($course);
+            }
+
+            // Override the language and timezone of the "current" user, so that
+            // mail is customised for the receiver.
+            cron_setup_user($user, $course);
+
+            // context lookups are already cached
+            $coursecontext = context_course::instance($course->id);
+            if (!is_enrolled($coursecontext, $user->id)) {
+                $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
+                mtrace(fullname($user)." not an active participant in " . $courseshortname);
+                continue;
+            }
+
+            if (!$grader = $DB->get_record("user", array("id"=>$submission->grader))) {
+                mtrace("Could not find grader $submission->grader");
+                continue;
+            }
+
+            if (!array_key_exists($submission->assignment, $modcache)) {
+                if (! $mod = get_coursemodule_from_instance("assign", $submission->assignment, $course->id)) {
+                    mtrace("Could not find course module for assignment id $submission->assignment");
+                    continue;
+                }
+                $modcache[$submission->assignment] = $mod;
+            } else {
+                $mod = $modcache[$submission->assignment];
+            }
+            // context lookups are already cached
+            $contextmodule = context_module::instance($mod->id);
+
+            if (!$mod->visible) {
+                // Hold mail notification for hidden assignments until later
+                continue;
+            }
+
+            // need to send this to the student
+            $messagetype = 'feedbackavailable';
+            $eventtype = 'assign_student_notification';
+            $updatetime = $submission->lastmodified;
+            $modulename = get_string('modulename', 'assign');
+            self::send_assignment_notification($grader, $user, $messagetype, $eventtype, $updatetime, $mod, $contextmodule, $course, $modulename, $submission->name);
+
+            $grade = new stdClass();
+            $grade->id = $submission->gradeid;
+            $grade->mailed = 1;
+            $DB->update_record('assign_grades', $grade);
+
+            mtrace('Done');
+        }
+        mtrace('Done processing ' . count($submissions) . ' assignment submissions');
 
+        cron_setup_user();
+
+        // Free up memory just to be sure
+        unset($courses);
+        unset($modcache);
 
         return true;
     }
@@ -2162,19 +2287,19 @@ class assign {
     /**
      * Returns a list of teachers that should be grading given submission
      *
-     * @param stdClass $user
+     * @param int $userid
      * @return array
      */
-    private function get_graders(stdClass $user) {
+    private function get_graders($userid) {
         //potential graders
-        $potentialgraders = get_users_by_capability($this->context, 'mod/assign:grade', '', '', '', '', '', '', false, false);
+        $potentialgraders = get_enrolled_users($this->context, "mod/assign:grade");
 
         $graders = array();
         if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) {   // Separate groups are being used
-            if ($groups = groups_get_all_groups($this->get_course()->id, $user->id)) {  // Try to find all groups
+            if ($groups = groups_get_all_groups($this->get_course()->id, $userid)) {  // Try to find all groups
                 foreach ($groups as $group) {
                     foreach ($potentialgraders as $grader) {
-                        if ($grader->id == $user->id) {
+                        if ($grader->id == $userid) {
                             continue; // do not send self
                         }
                         if (groups_is_member($group->id, $grader->id)) {
@@ -2185,7 +2310,7 @@ class assign {
             } else {
                 // user not in group, try to find graders without group
                 foreach ($potentialgraders as $grader) {
-                    if ($grader->id == $user->id) {
+                    if ($grader->id == $userid) {
                         continue; // do not send self
                     }
                     if (!groups_has_membership($this->get_course_module(), $grader->id)) {
@@ -2195,7 +2320,7 @@ class assign {
             }
         } else {
             foreach ($potentialgraders as $grader) {
-                if ($grader->id == $user->id) {
+                if ($grader->id == $userid) {
                     continue; // do not send self
                 }
                 // must be enrolled
@@ -2208,88 +2333,151 @@ class assign {
     }
 
     /**
-     * Creates the text content for emails to grader
+     * Format a notification for plain text
      *
-     * @param array $info The info used by the 'emailgradermail' language string
-     * @return string
+     * @param string $messagetype
+     * @param stdClass $info
+     * @param stdClass $course
+     * @param stdClass $context
+     * @param string $modulename
+     * @param string $assignmentname
      */
-    private function format_email_grader_text($info) {
-        $posttext  = format_string($this->get_course()->shortname, true, array('context' => $this->get_course_context())).' -> '.
-                     $this->get_module_name().' -> '.
-                     format_string($this->get_instance()->name, true, array('context' => $this->context))."\n";
+    private static function format_notification_message_text($messagetype, $info, $course, $context, $modulename, $assignmentname) {
+        $posttext  = format_string($course->shortname, true, array('context' => $context->get_course_context())).' -> '.
+                     $modulename.' -> '.
+                     format_string($assignmentname, true, array('context' => $context))."\n";
         $posttext .= '---------------------------------------------------------------------'."\n";
-        $posttext .= get_string("emailgradermail", "assign", $info)."\n";
+        $posttext .= get_string($messagetype . 'text', "assign", $info)."\n";
         $posttext .= "\n---------------------------------------------------------------------\n";
         return $posttext;
     }
 
-     /**
-     * Creates the html content for emails to graders
+    /**
+     * Format a notification for HTML
      *
-     * @param array $info The info used by the 'emailgradermailhtml' language string
-     * @return string
+     * @param string $messagetype
+     * @param stdClass $info
+     * @param stdClass $course
+     * @param stdClass $context
+     * @param string $modulename
+     * @param stdClass $coursemodule
+     * @param string $assignmentname
+     * @param stdClass $info
      */
-    private function format_email_grader_html($info) {
+    private static function format_notification_message_html($messagetype, $info, $course, $context, $modulename, $coursemodule, $assignmentname) {
         global $CFG;
         $posthtml  = '<p><font face="sans-serif">'.
-                     '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$this->get_course()->id.'">'.format_string($this->get_course()->shortname, true, array('context' => $this->get_course_context())).'</a> ->'.
-                     '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$this->get_course()->id.'">'.$this->get_module_name().'</a> ->'.
-                     '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$this->get_course_module()->id.'">'.format_string($this->get_instance()->name, true, array('context' => $this->context)).'</a></font></p>';
+                     '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.format_string($course->shortname, true, array('context' => $context->get_course_context())).'</a> ->'.
+                     '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$course->id.'">'.$modulename.'</a> ->'.
+                     '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$coursemodule->id.'">'.format_string($assignmentname, true, array('context' => $context)).'</a></font></p>';
         $posthtml .= '<hr /><font face="sans-serif">';
-        $posthtml .= '<p>'.get_string('emailgradermailhtml', 'assign', $info).'</p>';
+        $posthtml .= '<p>'.get_string($messagetype . 'html', 'assign', $info).'</p>';
         $posthtml .= '</font><hr />';
         return $posthtml;
     }
 
     /**
-     * email graders upon student submissions
+     * email someone about something (static so it can be called from cron)
      *
-     * @param stdClass $submission
+     * @param stdClass $userfrom
+     * @param stdClass $userto
+     * @param string $messagetype
+     * @param string $eventtype
+     * @param int $updatetime
+     * @param stdClass $coursemodule
+     * @param stdClass $context
+     * @param stdClass $course
+     * @param string $modulename
+     * @param string $assignmentname
      * @return void
      */
-    private function email_graders(stdClass $submission) {
-        global $CFG, $DB;
+    public static function send_assignment_notification($userfrom, $userto, $messagetype, $eventtype,
+                                                        $updatetime, $coursemodule, $context, $course,
+                                                        $modulename, $assignmentname) {
+        global $CFG;
 
-        if (empty($this->get_instance()->sendnotifications)) {          // No need to do anything
-            return;
-        }
+        $info = new stdClass();
+        $info->username = fullname($userfrom, true);
+        $info->assignment = format_string($assignmentname,true, array('context'=>$context));
+        $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id;
+        $info->timeupdated = strftime('%c',$updatetime);
 
-        $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
+        $postsubject = get_string($messagetype . 'small', 'assign', $info);
+        $posttext = self::format_notification_message_text($messagetype, $info, $course, $context, $modulename, $assignmentname);
+        $posthtml = ($userto->mailformat == 1) ? self::format_notification_message_html($messagetype, $info, $course, $context, $modulename, $coursemodule, $assignmentname) : '';
 
-        if ($teachers = $this->get_graders($user)) {
+        $eventdata = new stdClass();
+        $eventdata->modulename       = 'assign';
+        $eventdata->userfrom         = $userfrom;
+        $eventdata->userto           = $userto;
+        $eventdata->subject          = $postsubject;
+        $eventdata->fullmessage      = $posttext;
+        $eventdata->fullmessageformat = FORMAT_PLAIN;
+        $eventdata->fullmessagehtml  = $posthtml;
+        $eventdata->smallmessage     = $postsubject;
 
-            $strassignments = $this->get_module_name_plural();
-            $strassignment  = $this->get_module_name();
-            $strsubmitted  = get_string('submitted', 'assign');
+        $eventdata->name            = $eventtype;
+        $eventdata->component       = 'mod_assign';
+        $eventdata->notification    = 1;
+        $eventdata->contexturl      = $info->url;
+        $eventdata->contexturlname  = $info->assignment;
 
-            foreach ($teachers as $teacher) {
-                $info = new stdClass();
-                $info->username = fullname($user, true);
-                $info->assignment = format_string($this->get_instance()->name,true, array('context'=>$this->get_context()));
-                $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$this->get_course_module()->id;
-                $info->timeupdated = strftime('%c',$submission->timemodified);
+        message_send($eventdata);
+    }
 
-                $postsubject = $strsubmitted.': '.$info->username.' -> '.$this->get_instance()->name;
-                $posttext = $this->format_email_grader_text($info);
-                $posthtml = ($teacher->mailformat == 1) ? $this->format_email_grader_html($info) : '';
+    /**
+     * email someone about something
+     *
+     * @param stdClass $userfrom
+     * @param stdClass $userto
+     * @param string $messagetype
+     * @param string $eventtype
+     * @param int $updatetime
+     * @return void
+     */
+    public function send_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime) {
+        self::send_assignment_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime, $this->get_course_module(), $this->get_context(), $this->get_course(), $this->get_module_name(), $this->get_instance()->name);
+    }
 
-                $eventdata = new stdClass();
-                $eventdata->modulename       = 'assign';
-                $eventdata->userfrom         = $user;
-                $eventdata->userto           = $teacher;
-                $eventdata->subject          = $postsubject;
-                $eventdata->fullmessage      = $posttext;
-                $eventdata->fullmessageformat = FORMAT_PLAIN;
-                $eventdata->fullmessagehtml  = $posthtml;
-                $eventdata->smallmessage     = $postsubject;
+    /**
+     * Email student upon successful submission
+     *
+     * @global moodle_database $DB
+     * @param stdClass $submission
+     * @return void
+     */
+    private function email_student_submission_receipt(stdClass $submission) {
+        global $DB;
 
-                $eventdata->name            = 'assign_updates';
-                $eventdata->component       = 'mod_assign';
-                $eventdata->notification    = 1;
-                $eventdata->contexturl      = $info->url;
-                $eventdata->contexturlname  = $info->assignment;
+        $adminconfig = $this->get_admin_config();
+        if (!$adminconfig->submissionreceipts) {
+            // No need to do anything
+            return;
+        }
+        $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
+        $this->send_notification($user, $user, 'submissionreceipt', 'assign_student_notification', $submission->timemodified);
+    }
 
-                message_send($eventdata);
+    /**
+     * Email graders upon student submissions
+     *
+     * @global moodle_database $DB
+     * @param stdClass $submission
+     * @return void
+     */
+    private function email_graders(stdClass $submission) {
+        global $DB;
+
+        $late = $this->get_instance()->duedate && ($this->get_instance()->duedate < time());
+
+        if (!$this->get_instance()->sendnotifications && !($late && $this->get_instance()->sendlatenotifications)) {          // No need to do anything
+            return;
+        }
+
+        $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
+        if ($teachers = $this->get_graders($user->id)) {
+            foreach ($teachers as $teacher) {
+                $this->send_notification($user, $teacher, 'gradersubmissionupdated', 'assign_grader_notification', $submission->timemodified);
             }
         }
     }
@@ -2318,6 +2506,7 @@ class assign {
             $this->update_submission($submission);
             $this->add_to_log('submit for grading', $this->format_submission_for_log($submission));
             $this->email_graders($submission);
+            $this->email_student_submission_receipt($submission);
         }
     }
 
@@ -2549,6 +2738,7 @@ class assign {
             $this->add_to_log('submit', $this->format_submission_for_log($submission));
 
             if (!$this->get_instance()->submissiondrafts) {
+                $this->email_student_submission_receipt($submission);
                 $this->email_graders($submission);
             }
             return true;
@@ -2998,6 +3188,9 @@ class assign {
                 }
             }
             $this->process_outcomes($userid, $formdata);
+
+            $grade->mailed = 0;
+
             $this->update_grade($grade);
 
             $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
index f00d4d3..8ad624c 100644 (file)
@@ -92,6 +92,10 @@ class mod_assign_mod_form extends moodleform_mod {
         $mform->addElement('selectyesno', 'sendnotifications', get_string('sendnotifications', 'assign'));
         $mform->addHelpButton('sendnotifications', 'sendnotifications', 'assign');
         $mform->setDefault('sendnotifications', 1);
+        $mform->addElement('selectyesno', 'sendlatenotifications', get_string('sendlatenotifications', 'assign'));
+        $mform->addHelpButton('sendlatenotifications', 'sendlatenotifications', 'assign');
+        $mform->setDefault('sendlatenotifications', 1);
+        $mform->disabledIf('sendlatenotifications', 'sendnotifications', 'eq', 1);
 
         // plagiarism enabling form
         if (!empty($CFG->enableplagiarism)) {
index 206cacc..7860b64 100644 (file)
@@ -36,9 +36,6 @@ $ADMIN->add('assignmentplugins', new admin_category('assignfeedbackplugins',
 $ADMIN->add('assignfeedbackplugins', new assign_admin_page_manage_assign_plugins('assignfeedback'));
 
 
-assign_plugin_manager::add_admin_assign_plugin_settings('assignsubmission', $ADMIN, $settings, $module);
-assign_plugin_manager::add_admin_assign_plugin_settings('assignfeedback', $ADMIN, $settings, $module);
-
 if ($ADMIN->fulltree) {
     $menu = array();
     foreach (get_plugin_list('assignfeedback') as $type => $notused) {
@@ -55,6 +52,6 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configcheckbox('assign/showrecentsubmissions',
                    new lang_string('showrecentsubmissions', 'assign'),
                    new lang_string('configshowrecentsubmissions', 'assign'), 0));
-
-
-}
+    $settings->add(new admin_setting_configcheckbox('assign/submissionreceipts',
+                   get_string('sendsubmissionreceipts', 'mod_assign'), get_string('sendsubmissionreceipts_help', 'mod_assign'), 1));
+}
\ No newline at end of file
index 36b2036..c50e873 100644 (file)
@@ -29,6 +29,12 @@ require_once($CFG->dirroot.'/mod/assign/locallib.php');
 /** Include accesslib.php */
 require_once($CFG->libdir.'/accesslib.php');
 
+/**
+ * The maximum amount of time to spend upgrading a single assignment.
+ * This is intentionally generous (5 mins) as the effect of a timeout
+ * for a legitimate upgrade would be quite harsh (roll back code will not run)
+ */
+define('ASSIGN_MAX_UPGRADE_TIME_SECS', 300);
 
 /**
  * Class to manage upgrades from mod_assignment to mod_assign
@@ -48,9 +54,7 @@ class assign_upgrade_manager {
      * @return bool true or false
      */
     public function upgrade_assignment($oldassignmentid, & $log) {
-        global $DB, $CFG;
         // steps to upgrade an assignment
-
         global $DB, $CFG, $USER;
         // steps to upgrade an assignment
 
@@ -59,6 +63,9 @@ class assign_upgrade_manager {
               return false;
         }
 
+        // should we use a shutdown handler to rollback on timeout?
+        @set_time_limit(ASSIGN_MAX_UPGRADE_TIME_SECS);
+
 
         // get the module details
         $oldmodule = $DB->get_record('modules', array('name'=>'assignment'), '*', MUST_EXIST);
@@ -77,6 +84,7 @@ class assign_upgrade_manager {
         $data->introformat = $oldassignment->introformat;
         $data->alwaysshowdescription = 1;
         $data->sendnotifications = $oldassignment->emailteachers;
+        $data->sendlatenotifications = $oldassignment->emailteachers;
         $data->duedate = $oldassignment->timedue;
         $data->allowsubmissionsfromdate = $oldassignment->timeavailable;
         $data->grade = $oldassignment->grade;
@@ -220,7 +228,7 @@ class assign_upgrade_manager {
 
         } catch (Exception $exception) {
             $rollback = true;
-            $log .= get_string('conversionexception', 'mod_assign', $exception->getMessage());
+            $log .= get_string('conversionexception', 'mod_assign', $exception->error);
         }
 
         if ($rollback) {
index 82262a5..b2b2bfa 100644 (file)
@@ -24,9 +24,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->component = 'mod_assign';
-$module->version  = 2012050300;
-$module->requires = 2012050300;  // Requires this Moodle version
+$module->component = 'mod_assign'; // Full name of the plugin (used for diagnostics)
+$module->version  = 2012051700;    // The current module version (Date: YYYYMMDDXX)
+$module->requires = 2012050300;    // Requires this Moodle version
 $module->cron     = 60;
 
 
index 290558b..fe2053d 100644 (file)
@@ -75,7 +75,7 @@ class data_field_picture extends data_field_base {
         $options = new stdClass();
         $options->maxbytes  = $this->field->param3;
         $options->itemid    = $itemid;
-        $options->accepted_types = array('image');
+        $options->accepted_types = array('web_image');
         $options->return_types = FILE_INTERNAL;
         $options->context = $PAGE->context;
         if (!empty($file)) {
index a15dfb2..089a98d 100644 (file)
@@ -82,7 +82,7 @@ class data_field_textarea extends data_field_base {
         if ($options['maxfiles'] != 0 ) {
             $args = new stdClass();
             // need these three to filter repositories list
-            $args->accepted_types = array('image');
+            $args->accepted_types = array('web_image');
             $args->return_types = (FILE_INTERNAL | FILE_EXTERNAL);
             $args->context = $this->context;
             $args->env = 'filepicker';
index 48e5d76..b078a6d 100644 (file)
@@ -300,8 +300,8 @@ $string['oldpostdays'] = 'Read after days';
 $string['openmode0'] = 'No discussions, no replies';
 $string['openmode1'] = 'No discussions, but replies are allowed';
 $string['openmode2'] = 'Discussions and replies are allowed';
-$string['overviewnumpostssince'] = 'posts since last login';
-$string['overviewnumunread'] = 'total unread';
+$string['overviewnumpostssince'] = '{$a} posts since last login';
+$string['overviewnumunread'] = '{$a} total unread';
 $string['page-mod-forum-x'] = 'Any forum module page';
 $string['page-mod-forum-view'] = 'Forum module main page';
 $string['page-mod-forum-discuss'] = 'Forum module discussion thread page';
index b2f1410..9bcc409 100644 (file)
@@ -1380,8 +1380,6 @@ function forum_print_overview($courses,&$htmlarray) {
     }
 
     $strforum = get_string('modulename','forum');
-    $strnumunread = get_string('overviewnumunread','forum');
-    $strnumpostssince = get_string('overviewnumpostssince','forum');
 
     foreach ($forums as $forum) {
         $str = '';
@@ -1400,9 +1398,9 @@ function forum_print_overview($courses,&$htmlarray) {
             $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
                 $forum->name.'</a></div>';
             $str .= '<div class="info"><span class="postsincelogin">';
-            $str .= $count.' '.$strnumpostssince."</span>";
+            $str .= get_string('overviewnumpostssince', 'forum', $count)."</span>";
             if (!empty($showunread)) {
-                $str .= '<div class="unreadposts">'.$thisunread .' '.$strnumunread.'</div>';
+                $str .= '<div class="unreadposts">'.get_string('overviewnumunread', 'forum', $thisunread).'</div>';
             }
             $str .= '</div></div>';
         }
index 64f7fcc..e2d9758 100644 (file)
@@ -82,7 +82,7 @@ function resource_display_embed($resource, $cm, $course, $file) {
         core_media::OPTION_BLOCK => true,
     );
 
-    if (in_array($mimetype, array('image/gif','image/jpeg','image/png'))) {  // It's an image
+    if (file_mimetype_in_typegroup($mimetype, 'web_image')) {  // It's an image
         $code = resourcelib_embed_image($fullurl, $title);
 
     } else if ($mimetype === 'application/pdf') {
@@ -409,30 +409,22 @@ function resource_print_filenotfound($resource, $cm, $course) {
  * @return int display type constant
  */
 function resource_get_final_display_type($resource) {
-    global $CFG;
+    global $CFG, $PAGE;
 
     if ($resource->display != RESOURCELIB_DISPLAY_AUTO) {
         return $resource->display;
     }
 
-    static $download = array('application/zip', 'application/x-tar', 'application/g-zip');    // binary formats
-    static $embed    = array('image/gif', 'image/jpeg', 'image/png', 'image/svg+xml',         // images
-                             'application/x-shockwave-flash', 'video/x-flv', 'video/x-ms-wm', // video formats
-                             'video/quicktime', 'video/mpeg', 'video/mp4',
-                             'audio/mp3', 'audio/x-realaudio-plugin', 'x-realaudio-plugin',   // audio formats
-                             'application/pdf', 'text/html',
-                            );
-
     if (empty($resource->mainfile)) {
         return RESOURCELIB_DISPLAY_DOWNLOAD;
     } else {
         $mimetype = mimeinfo('type', $resource->mainfile);
     }
 
-    if (in_array($mimetype, $download)) {
+    if (file_mimetype_in_typegroup($mimetype, 'archive')) {
         return RESOURCELIB_DISPLAY_DOWNLOAD;
     }
-    if (in_array($mimetype, $embed)) {
+    if (file_mimetype_in_typegroup($mimetype, array('web_image', '.pdf', '.htm', 'web_video', 'web_audio'))) {
         return RESOURCELIB_DISPLAY_EMBED;
     }
 
index b213a94..9992dde 100644 (file)
@@ -39,6 +39,9 @@ class mod_wiki_edit_form extends moodleform {
         global $CFG;
 
         $mform = $this->_form;
+        // BEWARE HACK: In order for things to work we need to override the form id and set it to mform1.
+        // The first form to be instantiated never gets displayed so this should be safe.
+        $mform->updateAttributes(array('id' => 'mform1'));
 
         $version = $this->_customdata['version'];
         $format = $this->_customdata['format'];
index 8cb6383..e2e48fd 100644 (file)
Binary files a/pix/f/folder-128.png and b/pix/f/folder-128.png differ
index def1359..b5b25fb 100644 (file)
Binary files a/pix/f/folder-24.png and b/pix/f/folder-24.png differ
index 06f1e7b..98362b0 100644 (file)
Binary files a/pix/f/folder-32.png and b/pix/f/folder-32.png differ
index 326aa68..fcc0d42 100644 (file)
Binary files a/pix/f/folder-48.png and b/pix/f/folder-48.png differ
index ed9aa8c..fcffba6 100644 (file)
Binary files a/pix/f/folder-64.png and b/pix/f/folder-64.png differ
index abb6b49..392947e 100644 (file)
Binary files a/pix/f/folder-open-128.png and b/pix/f/folder-open-128.png differ
index a38a9ca..be120b0 100644 (file)
Binary files a/pix/f/folder-open-24.png and b/pix/f/folder-open-24.png differ
index 3940796..17d9afc 100644 (file)
Binary files a/pix/f/folder-open-32.png and b/pix/f/folder-open-32.png differ
index 487dbb1..50ce8e2 100644 (file)
Binary files a/pix/f/folder-open-48.png and b/pix/f/folder-open-48.png differ
index 92f81db..bdcc193 100644 (file)
Binary files a/pix/f/folder-open-64.png and b/pix/f/folder-open-64.png differ
index f81d49c..f64f263 100644 (file)
Binary files a/pix/f/folder-open.png and b/pix/f/folder-open.png differ
index 2cdcf73..8983b4e 100644 (file)
Binary files a/pix/f/folder.png and b/pix/f/folder.png differ
index 4abad67..fa7eaf3 100644 (file)
@@ -171,7 +171,7 @@ class repository_upload extends repository {
 
         if ($this->mimetypes != '*') {
             // check filetype
-            $filemimetype = mimeinfo('type', $_FILES[$elname]['name']);
+            $filemimetype = file_storage::mimetype($_FILES[$elname]['tmp_name']);
             if (!in_array($filemimetype, $this->mimetypes)) {
                 throw new moodle_exception('invalidfiletype', 'repository', '', get_mimetype_description(array('filename' => $_FILES[$elname]['name'])));
             }
index 1b04f9d..ee0f8c0 100644 (file)
@@ -210,7 +210,7 @@ a.skip:active {position: static;display: block;}
 .mform .fitem .fitemtitle {width:15%;text-align:right;float:left;}
 .mform .fitem .fitemtitle div {display: inline;}
 .mform .fitem .felement {border-width: 0;width:80%;margin-left:16%;}
-.mform .fitem fieldset.felement {margin-left:0;padding-left:1%;margin-bottom:0}
+.mform .fitem fieldset.felement {margin-left:15%;padding-left:1%;margin-bottom:0}
 .mform .error,
 .mform .required {color:#A00;}
 .mform .required .fgroup span label {color:#000;}
index 09f8983..5067068 100644 (file)
@@ -43,10 +43,10 @@ background: #CCC!important;filter: progid:DXImageTransform.Microsoft.gradient(st
 /*
  * Tools, Path & View on fp-navbar (File Picker and File Manager)
  */
-.fp-toolbar {display: table-row;line-height: 22px;float:left;}
+.fp-toolbar {/*display: table-row;*/display: block;line-height: 22px;float:left;}
 .fp-toolbar.empty {display:none;}
 .fp-toolbar .disabled {display:none;}
-.fp-toolbar div {display: inline-block;padding: 0px 2px;}
+.fp-toolbar div {display: inline-block;padding: 0px 2px;padding-right: 10px;}
 .fp-toolbar img {vertical-align: -15%; margin-right: 5px;}
 .file-picker .search-entry {background:#FFF url('[[pix:a/search]]') no-repeat 3px 3px;height:18px;width:230px;border: 1px solid #BBB;padding-left:20px;}
 
@@ -64,6 +64,7 @@ background: #CCC!important;filter: progid:DXImageTransform.Microsoft.gradient(st
 .fp-vb-tree {background:url('[[pix:theme|fp/view_tree_active]]') no-repeat 0px 0px;width:23px;height:22px;display: inline-block;margin-left: -4px;}
 .fp-vb-tree.checked {background:url('[[pix:theme|fp/view_tree_selected]]');}
 .fp-viewbar.disabled .fp-vb-tree {background:url('[[pix:theme|fp/view_tree_inactive]]');}
+.file-picker .fp-clear-right {clear: right;}
 
 /*
  * Icon view (File Picker and File Manager)
@@ -86,37 +87,36 @@ background: #CCC!important;filter: progid:DXImageTransform.Microsoft.gradient(st
 .file-picker .yui3-datatable-data .yui3-datatable-odd .yui3-datatable-sorted {background-color: #F6F6F6;}
 .file-picker .yui3-datatable-even .yui3-datatable-cell {background-color: #FFF;border-left: 0px solid #FFF;}
 .file-picker .yui3-datatable-data .yui3-datatable-even .yui3-datatable-sorted {background-color: #FFF;}
-.file-picker .fp-icon img {max-height:16px;max-width:16px;}
 
 /*
- * Tree view (File Picker and File Manager)
+ * Tree view (File Manager only)
  */
 /*.file-picker .fp-treeview .fp-folder .fp-icon, .filemanager .fp-treeview .fp-folder .fp-icon {}*/
 /* first or middle sibling, no children */
-/*.file-picker .ygtvtn, .filemanager*/ .ygtvtn {background: url('[[pix:moodle|y/tn]]') 0 0 no-repeat;width:17px;height:22px;}
+.file-picker .ygtvtn, .filemanager .ygtvtn {background: url('[[pix:moodle|y/tn]]') 0px 0px no-repeat;width:17px;height:22px;}
 /* first or middle sibling, collapsable */
-/*.file-picker .ygtvtm, .filemanager*/ .ygtvtm {background: url('[[pix:moodle|y/tm]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvtm, .filemanager .ygtvtm {background: url('[[pix:moodle|y/tm]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* first or middle sibling, collapsable, hover */
-/*.file-picker .ygtvtmh, .filemanager*/ .ygtvtmh {background: url('[[pix:moodle|y/tm]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvtmh, .filemanager .ygtvtmh {background: url('[[pix:moodle|y/tm]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* first or middle sibling, expandable */
-/*.file-picker .ygtvtp, .filemanager*/ .ygtvtp {background: url('[[pix:moodle|y/tp]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvtp, .filemanager .ygtvtp {background: url('[[pix:moodle|y/tp]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* first or middle sibling, expandable, hover */
-/*.file-picker .ygtvtph, .filemanager*/ .ygtvtph {background: url('[[pix:moodle|y/tp]]') 0 0 no-repeat;width:13px;height:22px;cursor:pointer;}
+.file-picker .ygtvtph, .filemanager .ygtvtph {background: url('[[pix:moodle|y/tp]]') 0px 10px no-repeat;width:13px;height:22px;cursor:pointer;}
 /* last sibling, no children */
-/*.file-picker .ygtvln, .filemanager*/ .ygtvln {background: url('[[pix:moodle|y/ln]]') 0 0 no-repeat;width:17px;height:22px;}
+.file-picker .ygtvln, .filemanager .ygtvln {background: url('[[pix:moodle|y/ln]]') 0px 0px no-repeat;width:17px;height:22px;}
 /* Last sibling, collapsable */
-/*.file-picker .ygtvlm, .filemanager*/ .ygtvlm {background: url('[[pix:moodle|y/lm]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvlm, .filemanager .ygtvlm {background: url('[[pix:moodle|y/lm]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* Last sibling, collapsable, hover */
-/*.file-picker .ygtvlmh, .filemanager*/ .ygtvlmh {background: url('[[pix:moodle|y/lm]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvlmh, .filemanager .ygtvlmh {background: url('[[pix:moodle|y/lm]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* Last sibling, expandable */
-/*.file-picker .ygtvlp, .filemanager*/ .ygtvlp {background: url('[[pix:moodle|y/lp]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvlp, .filemanager .ygtvlp {background: url('[[pix:moodle|y/lp]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* Last sibling, expandable, hover */
-/*.file-picker .ygtvlph, .filemanager*/ .ygtvlph {background: url('[[pix:moodle|y/lp]]') 0 0 no-repeat;width:13px;height:12px;cursor:pointer;}
+.file-picker .ygtvlph, .filemanager .ygtvlph {background: url('[[pix:moodle|y/lp]]') 0px 10px no-repeat;width:13px;height:12px;cursor:pointer;}
 /* Loading icon */
-/*.file-picker .ygtvloading, .filemanager*/ .ygtvloading {background: url('[[pix:moodle|y/loading]]') 0 0 no-repeat;width:16px;height:22px;}
+.file-picker .ygtvloading, .filemanager .ygtvloading {background: transparent url('[[pix:moodle|y/loading]]') 0 0 no-repeat;width:16px;height:22px;}
 /* the style for the empty cells that are used for rendering the depth* of the node */
-/*.file-picker .ygtvdepthcell, .filemanager*/ .ygtvdepthcell {background: url('[[pix:moodle|y/vline]]') 0 0 no-repeat;width:17px;height:22px;}
-/*.file-picker .ygtvblankdepthcell, .filemanager*/ .ygtvblankdepthcell {width:17px;height:22px;}
+.file-picker .ygtvdepthcell, .filemanager .ygtvdepthcell {background: url('[[pix:moodle|y/vline]]') 0 0 no-repeat;width:17px;height:/*22px;*/32px;}
+.file-picker .ygtvblankdepthcell, .filemanager .ygtvblankdepthcell {width:17px;height:22px;}
 /* the style of the div around each node */
 /*.file-picker .ygtvitem  table, .filemanager .ygtvitem  table{margin-bottom:0;}*/
 /*.file-picker .ygtvitem  td, .filemanager .ygtvitem  td {border:none;padding:0;}*/
@@ -127,7 +127,12 @@ background: #CCC!important;filter: progid:DXImageTransform.Microsoft.gradient(st
 .filemanager .ygtvlabel,.filemanager .ygtvlabel:link,.filemanager .ygtvlabel:visited,.filemanager .ygtvlabel:hover {margin-left:2px;text-decoration: none;}*/
 a.ygtvspacer:hover {color: transparent;text-decoration: none;}
 .ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover {background-color: transparent;cursor: pointer;margin-left: 2px;text-decoration: none;}
-/*.file-picker*/ .ygtvfocus {background-color: #DDD;}
+.file-picker .ygtvfocus, .filemanager .ygtvfocus {background-color: #EEE;}
+
+.fp-filename-icon {margin-top: 10px;display: block;/*top: -10px;position: relative;*/}
+.fp-icon {float: left;margin-top: -7px;width: 24px;height: 24px;margin-right: 10px;text-align: center;line-height: 24px;}
+.fp-icon img {max-height:24px;max-width:24px;vertical-align: middle;}
+.fp-filename {padding-right: 10px;}
 
 /*
  * Repositories Login on fp-content (File Picker only)
@@ -228,7 +233,7 @@ a.ygtvspacer:hover {color: transparent;text-decoration: none;}
 /*.filemanager-container ul{margin:0;padding:0;}
 .filemanager-container ul li{white-space:nowrap;list-style-type:none;}
 .filemanager-container ul li a{padding:0}*/
-.filemanager .fp-content{overflow: auto;max-height: 400px;}
+.filemanager .fp-content{overflow: auto;max-height: 472px;}
 .filemanager-container, .filepicker-filelist {overflow:hidden;}
 
 /*
@@ -256,14 +261,15 @@ a.ygtvspacer:hover {color: transparent;text-decoration: none;}
 .filemanager .fp-contextmenu {display:none;}
 .filemanager .fp-iconview .fp-folder.fp-hascontextmenu .fp-contextmenu {display:block;position:absolute;right:7px;bottom:5px;z-index: 2000;}
 .filemanager .fp-treeview .fp-folder.fp-hascontextmenu .fp-contextmenu,
-.filemanager .fp-tableview .fp-folder.fp-hascontextmenu .fp-contextmenu {display:inline;}
+.filemanager .fp-tableview .fp-folder.fp-hascontextmenu .fp-contextmenu {/*display: inline;*//*position: relative;left: 14px;margin-right: -20px;top: 6px;background: yellow;*/}
 
 /*
  * Drag and drop support (File Manager only)
  */
+.filepicker-filelist .filepicker-container,
 .filemanager.fm-noitems .fm-empty-container {display:block;position:absolute;top:10px;bottom:10px;left:10px;right:10px;border: 2px dashed #BBB;padding-top:85px;text-align:center;z-index: 3000;}
 .filepicker-filelist .dndupload-target,
-.filemanager-container .dndupload-target {background:#FFF;position:absolute;top:10px;bottom:10px;left:10px;right:10px;border: 2px dashed #fb7979;padding-top:85px;text-align:center;z-index:3000;-webkit-box-shadow:  0px 0px 0px 10px #FFF;-moz-box-shadow: 0px 0px 0px 10px #FFF;box-shadow:  0px 0px 0px 10px #FFF;z-index: 3000;}
+.filemanager-container .dndupload-target {background:#FFF;position:absolute;top:10px;bottom:10px;left:10px;right:10px;border: 2px dashed #fb7979;padding-top:85px;text-align:center;z-index:3000;-webkit-box-shadow:  0px 0px 0px 10px #FFF;-moz-box-shadow: 0px 0px 0px 10px #FFF;box-shadow:  0px 0px 0px 10px #FFF;}
 .filepicker-filelist.dndupload-over .dndupload-target,
 .filemanager-container.dndupload-over .dndupload-target {background:#FFF;position:absolute;top:10px;bottom:10px;left:10px;right:10px;border: 2px dashed #6c8cd3;padding-top:85px;text-align:center;z-index: 3000;}
 .dndupload-message {display:none;}
@@ -310,7 +316,7 @@ a.ygtvspacer:hover {color: transparent;text-decoration: none;}
 /*
  * Create folder dialogue (File Manager only)
  */
-.filemanager .fp-mkdir-dlg {text-align: center;}
+.filemanager.fp-mkdir-dlg {text-align: center;z-index: 999999}
 .filemanager .fp-mkdir-dlg p {text-align: left;margin: 40px 20px 0px;}
 .filemanager .fp-mkdir-dlg input {margin: 0px 20px 20px;}