Merge branch 'MDL-35412-23' of git://github.com/FMCorz/moodle into MOODLE_23_STABLE
authorSam Hemelryk <sam@moodle.com>
Tue, 16 Oct 2012 09:41:31 +0000 (17:41 +0800)
committerSam Hemelryk <sam@moodle.com>
Tue, 16 Oct 2012 09:41:31 +0000 (17:41 +0800)
49 files changed:
admin/tool/customlang/styles.css
admin/webservice/service_users.php
blocks/community/forms.php
blocks/dock.js
blocks/online_users/block_online_users.php
cohort/lib.php
comment/locallib.php
course/externallib.php
course/view.php
enrol/externallib.php
enrol/manual/externallib.php
lang/en/role.php
lib/adminlib.php
lib/blocklib.php
lib/completionlib.php
lib/db/access.php
lib/db/upgrade.php
lib/filebrowser/file_browser.php
lib/filebrowser/file_info.php
lib/filebrowser/file_info_context_course.php
lib/filebrowser/file_info_context_coursecat.php
lib/filebrowser/file_info_context_module.php
lib/filebrowser/file_info_stored.php
lib/navigationlib.php
lib/phpunit/classes/data_generator.php
lib/phpunit/tests/generator_test.php
lib/pluginlib.php
lib/upgradelib.php
mod/assign/gradeform.php
mod/book/locallib.php
mod/data/field/date/field.class.php
mod/data/lang/en/data.php
mod/data/locallib.php
mod/forum/locallib.php
mod/glossary/locallib.php
mod/imscp/locallib.php
mod/lesson/importppt.php [deleted file]
mod/lesson/importpptlib.php [deleted file]
mod/lesson/renderer.php
mod/lesson/version.php
mod/workshop/fileinfolib.php
repository/filepicker.js
repository/lib.php
repository/local/lib.php
theme/canvas/layout/frontpage.php
theme/canvas/layout/general.php
theme/canvas/layout/report.php
version.php
webservice/lib.php

index c743bcf..963b289 100644 (file)
@@ -67,3 +67,7 @@
 #page-admin-tool-customlang-index .continuebutton {
     margin-top: 1em;
 }
+
+.path-admin-tool-customlang #translator .standard.master.cell.c2 {
+    word-break: break-all;
+}
index 12db2b0..ad67c66 100644 (file)
@@ -97,7 +97,7 @@ $usersmissingcaps = $webservicemanager->get_missing_capabilities_by_users($allow
 
 //add the missing capabilities to the allowed users object to be displayed by renderer
 foreach ($allowedusers as &$alloweduser) {
-    if (!is_siteadmin($alloweduser->id) and key_exists($alloweduser->id, $usersmissingcaps)) {
+    if (!is_siteadmin($alloweduser->id) and array_key_exists($alloweduser->id, $usersmissingcaps)) {
         $alloweduser->missingcapabilities = implode(', ', $usersmissingcaps[$alloweduser->id]);
     }
 }
index fafab8a..5026d5b 100644 (file)
@@ -140,7 +140,7 @@ class community_hub_search_form extends moodleform {
             $options = array();
             $firsthub = false;
             foreach ($hubs as $hub) {
-                if (key_exists('id', $hub)) {
+                if (array_key_exists('id', $hub)) {
                     $params = array('hubid' => $hub['id'],
                         'filetype' => HUB_HUBSCREENSHOT_FILE_TYPE);
                     $imgurl = new moodle_url(HUB_HUBDIRECTORYURL .
index 4eff68c..3c8a31b 100644 (file)
@@ -958,7 +958,7 @@ M.core_dock.genericblock.prototype = {
         placeholder.replace(this.Y.Node.getDOMNode(this.cachedcontentnode));
         this.cachedcontentnode = this.Y.one('#'+this.cachedcontentnode.get('id'));
 
-        var commands = this.cachedcontentnode.one('.title .commands');
+        var commands = dockitem.commands;
         if (commands) {
             commands.all('.hidepanelicon').remove();
             commands.all('.moveto').remove();
index cbf1dac..2efe9a2 100644 (file)
@@ -31,7 +31,8 @@ class block_online_users extends block_base {
         if (isset($CFG->block_online_users_timetosee)) {
             $timetoshowusers = $CFG->block_online_users_timetosee * 60;
         }
-        $timefrom = 100 * floor((time()-$timetoshowusers) / 100); // Round to nearest 100 seconds for better query cache
+        $now = time();
+        $timefrom = 100 * floor(($now - $timetoshowusers) / 100); // Round to nearest 100 seconds for better query cache
 
         //Calculate if we are in separate groups
         $isseparategroups = ($this->page->course->groupmode == SEPARATEGROUPS
@@ -53,18 +54,23 @@ class block_online_users extends block_base {
         }
 
         $userfields = user_picture::fields('u', array('username'));
-
+        $params['now'] = $now;
+        $params['timefrom'] = $timefrom;
         if ($this->page->course->id == SITEID or $this->page->context->contextlevel < CONTEXT_COURSE) {  // Site-level
             $sql = "SELECT $userfields, MAX(u.lastaccess) AS lastaccess
                       FROM {user} u $groupmembers
-                     WHERE u.lastaccess > $timefrom
+                     WHERE u.lastaccess > :timefrom
+                           AND u.lastaccess <= :now
+                           AND u.deleted = 0
                            $groupselect
                   GROUP BY $userfields
                   ORDER BY lastaccess DESC ";
 
            $csql = "SELECT COUNT(u.id)
                       FROM {user} u $groupmembers
-                     WHERE u.lastaccess > $timefrom
+                     WHERE u.lastaccess > :timefrom
+                           AND u.lastaccess <= :now
+                           AND u.deleted = 0
                            $groupselect";
 
         } else {
@@ -77,9 +83,11 @@ class block_online_users extends block_base {
             $sql = "SELECT $userfields, MAX(ul.timeaccess) AS lastaccess
                       FROM {user_lastaccess} ul $groupmembers, {user} u
                       JOIN ($esqljoin) euj ON euj.id = u.id
-                     WHERE ul.timeaccess > $timefrom
+                     WHERE ul.timeaccess > :timefrom
                            AND u.id = ul.userid
                            AND ul.courseid = :courseid
+                           AND ul.timeaccess <= :now
+                           AND u.deleted = 0
                            $groupselect
                   GROUP BY $userfields
                   ORDER BY lastaccess DESC";
@@ -87,9 +95,11 @@ class block_online_users extends block_base {
            $csql = "SELECT COUNT(u.id)
                       FROM {user_lastaccess} ul $groupmembers, {user} u
                       JOIN ($esqljoin) euj ON euj.id = u.id
-                     WHERE ul.timeaccess > $timefrom
+                     WHERE ul.timeaccess > :timefrom
                            AND u.id = ul.userid
                            AND ul.courseid = :courseid
+                           AND ul.timeaccess <= :now
+                           AND u.deleted = 0
                            $groupselect";
 
             $params['courseid'] = $this->page->course->id;
@@ -138,7 +148,7 @@ class block_online_users extends block_base {
             }
             foreach ($users as $user) {
                 $this->content->text .= '<li class="listentry">';
-                $timeago = format_time(time() - $user->lastaccess); //bruno to calculate correctly on frontpage
+                $timeago = format_time($now - $user->lastaccess); //bruno to calculate correctly on frontpage
 
                 if (isguestuser($user)) {
                     $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16));
index 367b19e..4ab8ab7 100644 (file)
@@ -126,7 +126,7 @@ function cohort_delete_category($category) {
 }
 
 /**
- * Remove cohort member
+ * Add cohort member
  * @param  int $cohortid
  * @param  int $userid
  * @return void
@@ -143,7 +143,7 @@ function cohort_add_member($cohortid, $userid) {
 }
 
 /**
- * Add cohort member
+ * Remove cohort member
  * @param  int $cohortid
  * @param  int $userid
  * @return void
index 5a8b0d7..31b2799 100644 (file)
@@ -94,7 +94,7 @@ class comment_manager {
      */
     private function setup_course($courseid) {
         global $PAGE, $DB;
-        if (!empty($this->course)) {
+        if (!empty($this->course) && $this->course->id == $courseid) {
             // already set, stop
             return;
         }
index b18c0a4..88fd0fa 100644 (file)
@@ -285,7 +285,7 @@ class core_course_external extends external_api {
                         array('options' => $options));
 
         //retrieve courses
-        if (!key_exists('ids', $params['options'])
+        if (!array_key_exists('ids', $params['options'])
                 or empty($params['options']['ids'])) {
             $courses = $DB->get_records('course');
         } else {
@@ -523,12 +523,12 @@ class core_course_external extends external_api {
             require_capability('moodle/course:create', $context);
 
             // Make sure lang is valid
-            if (key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
+            if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
                 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
             }
 
             // Make sure theme is valid
-            if (key_exists('forcetheme', $course)) {
+            if (array_key_exists('forcetheme', $course)) {
                 if (!empty($CFG->allowcoursethemes)) {
                     if (empty($availablethemes[$course['forcetheme']])) {
                         throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
@@ -547,10 +547,10 @@ class core_course_external extends external_api {
             //set default value for completion
             $courseconfig = get_config('moodlecourse');
             if (completion_info::is_enabled_for_site()) {
-                if (!key_exists('enablecompletion', $course)) {
+                if (!array_key_exists('enablecompletion', $course)) {
                     $course['enablecompletion'] = $courseconfig->enablecompletion;
                 }
-                if (!key_exists('completionstartonenrol', $course)) {
+                if (!array_key_exists('completionstartonenrol', $course)) {
                     $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
                 }
             } else {
index cb978d8..18f0313 100644 (file)
@@ -20,6 +20,7 @@
     $marker      = optional_param('marker',-1 , PARAM_INT);
     $switchrole  = optional_param('switchrole',-1, PARAM_INT);
     $modchooser  = optional_param('modchooser', -1, PARAM_BOOL);
+    $return      = optional_param('return', 0, PARAM_LOCALURL);
 
     $params = array();
     if (!empty($name)) {
             // Redirect to site root if Editing is toggled on frontpage
             if ($course->id == SITEID) {
                 redirect($CFG->wwwroot .'/?redirect=0');
+            } else if (!empty($return)) {
+                redirect($CFG->wwwroot . $return);
             } else {
                 $url = new moodle_url($PAGE->url, array('notifyeditingon' => 1));
                 redirect($url);
             // Redirect to site root if Editing is toggled on frontpage
             if ($course->id == SITEID) {
                 redirect($CFG->wwwroot .'/?redirect=0');
+            } else if (!empty($return)) {
+                redirect($CFG->wwwroot . $return);
             } else {
                 redirect($PAGE->url);
             }
index 7d2fb5a..7e8dd4c 100644 (file)
@@ -388,7 +388,7 @@ class core_role_external extends external_api {
             // throw an exception if user is not able to assign the role in this context
             $roles = get_assignable_roles($context, ROLENAME_SHORT);
 
-            if (!key_exists($assignment['roleid'], $roles)) {
+            if (!array_key_exists($assignment['roleid'], $roles)) {
                 throw new invalid_parameter_exception('Can not assign roleid='.$assignment['roleid'].' in contextid='.$assignment['contextid']);
             }
 
@@ -451,7 +451,7 @@ class core_role_external extends external_api {
 
             // throw an exception if user is not able to unassign the role in this context
             $roles = get_assignable_roles($context, ROLENAME_SHORT);
-            if (!key_exists($unassignment['roleid'], $roles)) {
+            if (!array_key_exists($unassignment['roleid'], $roles)) {
                 throw new invalid_parameter_exception('Can not unassign roleid='.$unassignment['roleid'].' in contextid='.$unassignment['contextid']);
             }
 
index 44d90ac..81907a8 100644 (file)
@@ -101,7 +101,7 @@ class enrol_manual_external extends external_api {
 
             //throw an exception if user is not able to assign the role
             $roles = get_assignable_roles($context);
-            if (!key_exists($enrolment['roleid'], $roles)) {
+            if (!array_key_exists($enrolment['roleid'], $roles)) {
                 $errorparams = new stdClass();
                 $errorparams->roleid = $enrolment['roleid'];
                 $errorparams->courseid = $enrolment['courseid'];
index ffbdec3..f9ab6a9 100644 (file)
@@ -121,6 +121,7 @@ $string['course:changesummary'] = 'Change course summary';
 $string['course:enrolconfig'] = 'Configure enrol instances in courses';
 $string['course:enrolreview'] = 'Review course enrolments';
 $string['course:ignorefilesizelimits'] = 'Use files larger than any file size restrictions';
+$string['course:isincompletionreports'] = 'Be shown on completion reports';
 $string['course:manageactivities'] = 'Manage activities';
 $string['course:managefiles'] = 'Manage files';
 $string['course:managegrades'] = 'Manage grades';
index 2e8121b..c74e557 100644 (file)
@@ -7561,7 +7561,7 @@ class admin_setting_managewebservicetokens extends admin_setting {
                         array(array('id' => $token->userid)), $token->serviceid);
 
                 if (!is_siteadmin($token->userid) and
-                        key_exists($token->userid, $usermissingcaps)) {
+                        array_key_exists($token->userid, $usermissingcaps)) {
                     $missingcapabilities = implode(', ',
                             $usermissingcaps[$token->userid]);
                     if (!empty($missingcapabilities)) {
index 47885c9..9413201 100644 (file)
@@ -454,7 +454,9 @@ class block_manager {
      * @return bool True if all of the blocks within that region are docked
      */
     public function region_completely_docked($region, $output) {
-        if (!$this->page->theme->enable_dock) {
+        global $CFG;
+        // If theme doesn't allow docking or allowblockstodock is not set, then return.
+        if (!$this->page->theme->enable_dock || empty($CFG->allowblockstodock)) {
             return false;
         }
 
index fcd5910..fcc6f58 100644 (file)
@@ -1069,7 +1069,8 @@ class completion_info {
         global $DB;
 
         list($enrolledsql, $params) = get_enrolled_sql(
-                context_course::instance($this->course->id), '', $groupid, true);
+                context_course::instance($this->course->id),
+                'moodle/course:isincompletionreports', $groupid, true);
 
         $sql = 'SELECT u.id, u.firstname, u.lastname, u.idnumber';
         if ($extracontext) {
index 121750c..f2027df 100644 (file)
@@ -979,6 +979,14 @@ $capabilities = array(
         )
     ),
 
+    'moodle/course:isincompletionreports' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'student' => CAP_ALLOW,
+        ),
+    ),
+
     'moodle/course:viewscales' => array(
 
         'captype' => 'read',
index e617e1b..91263db 100644 (file)
@@ -969,5 +969,21 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012062502.03);
     }
 
+    if ($oldversion < 2012062502.07) {
+        // Find all orphaned blog associations that might exist.
+        $sql = "SELECT ba.id
+                  FROM {blog_association} ba
+             LEFT JOIN {post} p
+                    ON p.id = ba.blogid
+                 WHERE p.id IS NULL";
+        $orphanedrecordids = $DB->get_records_sql($sql);
+        // Now delete these associations.
+        foreach ($orphanedrecordids as $orphanedrecord) {
+            $DB->delete_records('blog_association', array('id' => $orphanedrecord->id));
+        }
+
+        upgrade_main_savepoint(true, 2012062502.07);
+    }
+
     return true;
 }
index f2105cb..d5ac755 100644 (file)
@@ -201,8 +201,13 @@ class file_browser {
     private function get_file_info_context_module($context, $component, $filearea, $itemid, $filepath, $filename) {
         global $COURSE, $DB, $CFG;
 
+        static $cachedmodules = array();
 
-        if (!$cm = get_coursemodule_from_id('', $context->instanceid)) {
+        if (!array_key_exists($context->instanceid, $cachedmodules)) {
+            $cachedmodules[$context->instanceid] = get_coursemodule_from_id('', $context->instanceid);
+        }
+
+        if (!($cm = $cachedmodules[$context->instanceid])) {
             return null;
         }
 
index d5bcade..a6af3f7 100644 (file)
@@ -88,6 +88,116 @@ abstract class file_info {
      */
     public abstract function get_children();
 
+    /**
+     * Builds SQL sub query (WHERE clause) for selecting files with the specified extensions
+     *
+     * If $extensions == '*' (any file), the result is array('', array())
+     * otherwise the result is something like array('AND filename ...', array(...))
+     *
+     * @param string|array $extensions - either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param string $prefix prefix for DB table files in the query (empty by default)
+     * @return array of two elements: $sql - sql where clause and $params - array of parameters
+     */
+    protected function build_search_files_sql($extensions, $prefix = null) {
+        global $DB;
+        if (strlen($prefix)) {
+            $prefix = $prefix.'.';
+        } else {
+            $prefix = '';
+        }
+        $sql = '';
+        $params = array();
+        if (is_array($extensions) && !in_array('*', $extensions)) {
+            $likes = array();
+            $cnt = 0;
+            foreach ($extensions as $ext) {
+                $cnt++;
+                $likes[] = $DB->sql_like($prefix.'filename', ':filename'.$cnt, false);
+                $params['filename'.$cnt] = '%'.$ext;
+            }
+            $sql .= ' AND (' . join(' OR ', $likes) . ')';
+        }
+        return array($sql, $params);
+     }
+
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * It is recommended to overwrite this function so it uses a proper SQL
+     * query and does not create unnecessary file_info objects (might require a lot of time
+     * and memory usage on big sites).
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        $list = $this->get_children();
+        $nonemptylist = array();
+        foreach ($list as $fileinfo) {
+            if ($fileinfo->is_directory()) {
+                if ($fileinfo->count_non_empty_children($extensions)) {
+                    $nonemptylist[] = $fileinfo;
+                }
+            } else if ($extensions === '*') {
+                $nonemptylist[] = $fileinfo;
+            } else {
+                $filename = $fileinfo->get_visible_name();
+                $extension = textlib::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+                if (!empty($extension) && in_array('.' . $extension, $extensions)) {
+                    $nonemptylist[] = $fileinfo;
+                }
+            }
+        }
+        return $nonemptylist;
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * We usually don't need the exact number of non empty children if it is >=2 (see param $limit)
+     * This function is used by repository_local to evaluate if the folder is empty. But
+     * it also can be used to check if folder has only one subfolder because in some cases
+     * this subfolder can be skipped.
+     *
+     * It is strongly recommended to overwrite this function so it uses a proper SQL
+     * query and does not create file_info objects (later might require a lot of time
+     * and memory usage on big sites).
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        $list = $this->get_children();
+        $cnt = 0;
+        // first loop through files
+        foreach ($list as $fileinfo) {
+            if (!$fileinfo->is_directory()) {
+                if ($extensions !== '*') {
+                    $filename = $fileinfo->get_visible_name();
+                    $extension = textlib::strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+                    if (empty($extension) || !in_array('.' . $extension, $extensions)) {
+                        continue;
+                    }
+                }
+                if ((++$cnt) >= $limit) {
+                    return $cnt;
+                }
+            }
+        }
+        // now loop through directories
+        foreach ($list as $fileinfo) {
+            if ($fileinfo->is_directory() && $fileinfo->count_non_empty_children($extensions)) {
+                if ((++$cnt) >= $limit) {
+                    return $cnt;
+                }
+            }
+        }
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
@@ -103,12 +213,12 @@ abstract class file_info {
     public function get_params_rawencoded() {
         $params = $this->get_params();
         $encoded = array();
-        $encoded[] = 'contextid='.$params['contextid'];
-        $encoded[] = 'component='.$params['component'];
-        $encoded[] = 'filearea='.$params['filearea'];
-        $encoded[] = 'itemid='.(is_null($params['itemid']) ? -1 : $params['itemid']);
-        $encoded[] = 'filepath='.(is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
-        $encoded[] = 'filename='.((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
+        $encoded[] = 'contextid=' . $params['contextid'];
+        $encoded[] = 'component=' . $params['component'];
+        $encoded[] = 'filearea=' . $params['filearea'];
+        $encoded[] = 'itemid=' . (is_null($params['itemid']) ? -1 : $params['itemid']);
+        $encoded[] = 'filepath=' . (is_null($params['filepath']) ? '' : rawurlencode($params['filepath']));
+        $encoded[] = 'filename=' . ((is_null($params['filename']) or $params['filename'] === '.') ? '' : rawurlencode($params['filename']));
 
         return $encoded;
     }
index 50876b3..5e61f56 100644 (file)
@@ -350,48 +350,90 @@ class file_info_context_course extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
-        $children = array();
+        return $this->get_filtered_children('*', false, true);
+    }
 
-        if ($child = $this->get_area_course_summary(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_course_section(null, null, null)) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_backup_section(null, null, null)) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_backup_course(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_backup_automated(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_course_legacy(0, '/', '.')) {
-            $children[] = $child;
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
+        $areas = array(
+            array('course', 'summary'),
+            array('course', 'section'),
+            array('backup', 'section'),
+            array('backup', 'course'),
+            array('backup', 'automated'),
+            array('course', 'legacy')
+        );
+        $children = array();
+        foreach ($areas as $area) {
+            if ($child = $this->get_file_info($area[0], $area[1], 0, '/', '.')) {
+                if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                    $children[] = $child;
+                    if (($countonly !== false) && count($children) >= $countonly) {
+                        return $countonly;
+                    }
+                }
+            }
         }
 
         if (!has_capability('moodle/course:managefiles', $this->context)) {
             // 'managefiles' capability is checked in every activity module callback.
             // Don't even waste time on retrieving the modules if we can't browse the files anyway
-            return $children;
-        }
-
-        // now list all modules
-        $modinfo = get_fast_modinfo($this->course);
-        foreach ($modinfo->cms as $cminfo) {
-            if (empty($cminfo->uservisible)) {
-                continue;
-            }
-            $modcontext = get_context_instance(CONTEXT_MODULE, $cminfo->id);
-            if ($child = $this->browser->get_file_info($modcontext)) {
-                $children[] = $child;
+        } else {
+            // now list all modules
+            $modinfo = get_fast_modinfo($this->course);
+            foreach ($modinfo->cms as $cminfo) {
+                if (empty($cminfo->uservisible)) {
+                    continue;
+                }
+                $modcontext = context_module::instance($cminfo->id, IGNORE_MISSING);
+                if ($child = $this->browser->get_file_info($modcontext)) {
+                    if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                        $children[] = $child;
+                        if (($countonly !== false) && count($children) >= $countonly) {
+                            return $countonly;
+                        }
+                    }
+                }
             }
         }
 
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
@@ -473,6 +515,37 @@ class file_info_area_course_legacy extends file_info_stored {
 
         return $result;
     }
+
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        if (!$this->lf->is_directory()) {
+            return array();
+        }
+
+        $result = array();
+        $fs = get_file_storage();
+
+        $storedfiles = $fs->get_directory_files($this->context->id, 'course', 'legacy', 0,
+                                                $this->lf->get_filepath(), false, true, "filepath, filename");
+        foreach ($storedfiles as $file) {
+            $extension = textlib::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
+            if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
+                $fileinfo = new file_info_area_course_legacy($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
+                                                 $this->itemidused, $this->readaccess, $this->writeaccess, false);
+                if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
+                    $result[] = $fileinfo;
+                }
+            }
+        }
+
+        return $result;
+    }
 }
 
 /**
@@ -578,6 +651,41 @@ class file_info_area_course_section extends file_info {
         return $children;
     }
 
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        $params1 = array(
+            'courseid' => $this->course->id,
+            'contextid' => $this->context->id,
+            'component' => 'course',
+            'filearea' => 'section',
+            'emptyfilename' => '.');
+        $sql1 = "SELECT DISTINCT cs.id FROM {files} f, {course_sections} cs
+            WHERE cs.course = :courseid
+            AND f.contextid = :contextid
+            AND f.component = :component
+            AND f.filearea = :filearea
+            AND f.itemid = cs.id
+            AND f.filename <> :emptyfilename";
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
+        $cnt = 0;
+        foreach ($rs as $record) {
+            if ((++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
@@ -689,6 +797,41 @@ class file_info_area_backup_section extends file_info {
         return $children;
     }
 
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        $params1 = array(
+            'courseid' => $this->course->id,
+            'contextid' => $this->context->id,
+            'component' => 'backup',
+            'filearea' => 'section',
+            'emptyfilename' => '.');
+        $sql1 = "SELECT DISTINCT cs.id sectionid FROM {files} f, {course_sections} cs
+            WHERE cs.course = :courseid
+            AND f.contextid = :contextid
+            AND f.component = :component
+            AND f.filearea = :filearea
+            AND f.itemid = cs.id
+            AND f.filename <> :emptyfilename";
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
+        $cnt = 0;
+        foreach ($rs as $record) {
+            if ((++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
index 99b6d1f..e8d542c 100644 (file)
@@ -188,6 +188,66 @@ class file_info_context_coursecat extends file_info {
         return $children;
     }
 
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        $cnt = 0;
+        if (($child = $this->get_area_coursecat_description(0, '/', '.'))
+                && $child->count_non_empty_children($extensions) && (++$cnt) >= $limit) {
+            return $cnt;
+        }
+
+        $rs = $DB->get_recordset_sql('SELECT ctx.id contextid, c.visible
+                FROM {context} ctx, {course} c
+                WHERE ctx.instanceid = c.id
+                AND ctx.contextlevel = :courselevel
+                AND c.category = :categoryid
+                ORDER BY c.visible DESC', // retrieve visible courses first
+                array('categoryid' => $this->category->id, 'courselevel' => CONTEXT_COURSE));
+        foreach ($rs as $record) {
+            $context = context::instance_by_id($record->contextid);
+            if (!$record->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
+                continue;
+            }
+            if (($child = $this->browser->get_file_info($context))
+                    && $child->count_non_empty_children($extensions) && (++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        if ($cnt >= $limit) {
+            return $cnt;
+        }
+
+        $rs = $DB->get_recordset_sql('SELECT ctx.id contextid, cat.visible
+                FROM {context} ctx, {course_categories} cat
+                WHERE ctx.instanceid = cat.id
+                AND ctx.contextlevel = :catlevel
+                AND cat.parent = :categoryid
+                ORDER BY cat.visible DESC', // retrieve visible categories first
+                array('categoryid' => $this->category->id, 'catlevel' => CONTEXT_COURSECAT));
+        foreach ($rs as $record) {
+            $context = context::instance_by_id($record->contextid);
+            if (!$record->visible and !has_capability('moodle/category:viewhiddencategories', $context)) {
+                continue;
+            }
+            if (($child = $this->browser->get_file_info($context))
+                    && $child->count_non_empty_children($extensions) && (++$cnt) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+
+        return $cnt;
+    }
+
     /**
      * Returns parent file_info instance
      *
index 0357ee3..f4b2e52 100644 (file)
@@ -41,6 +41,8 @@ class file_info_context_module extends file_info {
     protected $modname;
     /** @var array Available file areas */
     protected $areas;
+    /** @var array caches the result of last call to get_non_empty_children() */
+    protected $nonemptychildren;
 
     /**
      * Constructor
@@ -58,6 +60,7 @@ class file_info_context_module extends file_info {
         $this->course  = $course;
         $this->cm      = $cm;
         $this->modname = $modname;
+        $this->nonemptychildren = null;
 
         include_once("$CFG->dirroot/mod/$modname/lib.php");
 
@@ -258,24 +261,91 @@ class file_info_context_module extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
-        $children = array();
+        return $this->get_filtered_children('*', false, true);
+    }
 
-        if ($child = $this->get_area_backup(0, '/', '.')) {
-            $children[] = $child;
-        }
-        if ($child = $this->get_area_intro(0, '/', '.')) {
-            $children[] = $child;
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
+        global $DB;
+        // prepare list of areas including intro and backup
+        $areas = array(
+            array('mod_'.$this->modname, 'intro'),
+            array('backup', 'activity')
+        );
+        foreach ($this->areas as $area => $desctiption) {
+            $areas[] = array('mod_'.$this->modname, $area);
         }
 
-        foreach ($this->areas as $area=>$desctiption) {
-            if ($child = $this->get_file_info('mod_'.$this->modname, $area, null, null, null)) {
-                $children[] = $child;
+        $params1 = array('contextid' => $this->context->id, 'emptyfilename' => '.');
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $children = array();
+        foreach ($areas as $area) {
+            if (!$returnemptyfolders) {
+                // fast pre-check if there are any files in the filearea
+                $params1['component'] = $area[0];
+                $params1['filearea'] = $area[1];
+                if (!$DB->record_exists_sql('SELECT 1 from {files}
+                        WHERE contextid = :contextid
+                        AND filename <> :emptyfilename
+                        AND component = :component
+                        AND filearea = :filearea '.$sql2,
+                        array_merge($params1, $params2))) {
+                    continue;
+                }
+            }
+            if ($child = $this->get_file_info($area[0], $area[1], null, null, null)) {
+                if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                    $children[] = $child;
+                    if ($countonly !== false && count($children) >= $countonly) {
+                        break;
+                    }
+                }
             }
         }
-
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        if ($this->nonemptychildren !== null) {
+            return $this->nonemptychildren;
+        }
+        $this->nonemptychildren = $this->get_filtered_children($extensions);
+        return $this->nonemptychildren;
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        if ($this->nonemptychildren !== null) {
+            return count($this->nonemptychildren);
+        }
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
index 44fa566..e9caf4c 100644 (file)
@@ -350,6 +350,84 @@ class file_info_stored extends file_info {
         return $result;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        $result = array();
+        if (!$this->lf->is_directory()) {
+            return $result;
+        }
+
+        $fs = get_file_storage();
+
+        $storedfiles = $fs->get_directory_files($this->context->id, $this->lf->get_component(), $this->lf->get_filearea(), $this->lf->get_itemid(),
+                                                $this->lf->get_filepath(), false, true, "filepath, filename");
+        foreach ($storedfiles as $file) {
+            $extension = textlib::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
+            if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
+                $fileinfo = new file_info_stored($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
+                                                 $this->itemidused, $this->readaccess, $this->writeaccess, false);
+                if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
+                    $result[] = $fileinfo;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        global $DB;
+        if (!$this->lf->is_directory()) {
+            return 0;
+        }
+
+        $filepath = $this->lf->get_filepath();
+        $length = textlib::strlen($filepath);
+        $sql = "SELECT filepath, filename
+                  FROM {files} f
+                 WHERE f.contextid = :contextid AND f.component = :component AND f.filearea = :filearea AND f.itemid = :itemid
+                       AND ".$DB->sql_substr("f.filepath", 1, $length)." = :filepath
+                       AND filename <> '.' ";
+        $params = array('contextid' => $this->context->id,
+            'component' => $this->lf->get_component(),
+            'filearea' => $this->lf->get_filearea(),
+            'itemid' => $this->lf->get_itemid(),
+            'filepath' => $filepath);
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $rs = $DB->get_recordset_sql($sql.' '.$sql2, array_merge($params, $params2));
+        $children = array();
+        foreach ($rs as $record) {
+            // we don't need to check access to individual files here, since the user can access parent
+            if ($record->filepath === $filepath) {
+                $children[] = $record->filename;
+            } else {
+                $path = explode('/', textlib::substr($record->filepath, $length));
+                if (!in_array($path[0], $children)) {
+                    $children[] = $path[0];
+                }
+            }
+            if (count($children) >= $limit) {
+                break;
+            }
+        }
+        $rs->close();
+        return count($children);
+    }
+
     /**
      * Returns parent file_info instance
      *
index 9ba6f13..b95aae6 100644 (file)
@@ -3561,7 +3561,7 @@ class settings_navigation extends navigation_node {
                 $baseurl->param('sesskey', sesskey());
             } else {
                 // Edit on the main course page.
-                $baseurl = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));
+                $baseurl = new moodle_url('/course/view.php', array('id'=>$course->id, 'return'=>$this->page->url->out_as_local_url(false), 'sesskey'=>sesskey()));
             }
 
             $editurl = clone($baseurl);
index 8225f23..8bfd798 100644 (file)
@@ -156,9 +156,11 @@ EOD;
         }
 
         if (!isset($record['username'])) {
-            $record['username'] = textlib::strtolower($record['firstname']).textlib::strtolower($record['lastname']);
+            $record['username'] = 'username'.$i;
+            $j = 2;
             while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
-                $record['username'] = $record['username'].'_'.$i;
+                $record['username'] = 'username'.$i.'_'.$j;
+                $j++;
             }
         }
 
index 316ee70..3787b34 100644 (file)
@@ -44,6 +44,11 @@ class core_phpunit_generator_testcase extends advanced_testcase {
         $count = $DB->count_records('user');
         $user = $generator->create_user();
         $this->assertEquals($count+1, $DB->count_records('user'));
+        $this->assertSame($user->username, clean_param($user->username, PARAM_USERNAME));
+        $this->assertSame($user->email, clean_param($user->email, PARAM_EMAIL));
+        $user = $generator->create_user(array('firstname'=>'Žluťoučký', 'lastname'=>'Koníček'));
+        $this->assertSame($user->username, clean_param($user->username, PARAM_USERNAME));
+        $this->assertSame($user->email, clean_param($user->email, PARAM_EMAIL));
 
         $count = $DB->count_records('course_categories');
         $category = $generator->create_category();
index b224d6d..470fa88 100644 (file)
@@ -2514,12 +2514,4 @@ class plugininfo_local extends plugininfo_base {
     public function get_uninstall_url() {
         return new moodle_url('/admin/localplugins.php', array('delete' => $this->name, 'sesskey' => sesskey()));
     }
-
-    public function get_settings_url() {
-        if (file_exists($this->full_path('settings.php'))) {
-            return new moodle_url('/admin/settings.php', array('section' => 'local_' . $this->name));
-        } else {
-            return parent::get_settings_url();
-        }
-    }
 }
index c299ffe..cde67d9 100644 (file)
@@ -964,7 +964,7 @@ function external_update_descriptions($component) {
             $dbfunction->classpath = $function['classpath'];
             $update = true;
         }
-        $functioncapabilities = key_exists('capabilities', $function)?$function['capabilities']:'';
+        $functioncapabilities = array_key_exists('capabilities', $function)?$function['capabilities']:'';
         if ($dbfunction->capabilities != $functioncapabilities) {
             $dbfunction->capabilities = $functioncapabilities;
             $update = true;
@@ -980,7 +980,7 @@ function external_update_descriptions($component) {
         $dbfunction->methodname = $function['methodname'];
         $dbfunction->classpath  = empty($function['classpath']) ? null : $function['classpath'];
         $dbfunction->component  = $component;
-        $dbfunction->capabilities = key_exists('capabilities', $function)?$function['capabilities']:'';
+        $dbfunction->capabilities = array_key_exists('capabilities', $function)?$function['capabilities']:'';
         $dbfunction->id = $DB->insert_record('external_functions', $dbfunction);
     }
     unset($functions);
index 8f73ac1..366d5d8 100644 (file)
@@ -59,6 +59,17 @@ class mod_assign_grade_form extends moodleform {
         }
     }
 
+    /**
+     * This is required so when using "Save and next", each form is not defaulted to the previous form.
+     * Giving each form a unique identitifer is enough to prevent this (include the rownum in the form name).
+     *
+     * @return string - The unique identifier for this form.
+     */
+    protected function get_form_identifier() {
+        $params = $this->_customdata[2];
+        return get_class($this) . '_' . $params['rownum'];
+    }
+
     /**
      * Perform minimal validation on the grade form
      * @param array $data
index 26d0e40..9502805 100644 (file)
@@ -448,18 +448,84 @@ class book_file_info extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
+        return $this->get_filtered_children('*', false, true);
+    }
+
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
         global $DB;
+        $params = array('contextid' => $this->context->id,
+            'component' => 'mod_book',
+            'filearea' => $this->filearea,
+            'bookid' => $this->cm->instance);
+        $sql = 'SELECT DISTINCT bc.id, bc.pagenum
+                    FROM {files} f, {book_chapters} bc
+                    WHERE f.contextid = :contextid
+                    AND f.component = :component
+                    AND f.filearea = :filearea
+                    AND bc.bookid = :bookid
+                    AND bc.id = f.itemid';
+        if (!$returnemptyfolders) {
+            $sql .= ' AND filename <> :emptyfilename';
+            $params['emptyfilename'] = '.';
+        }
+        list($sql2, $params2) = $this->build_search_files_sql($extensions, 'f');
+        $sql .= ' '.$sql2;
+        $params = array_merge($params, $params2);
+        if ($countonly === false) {
+            $sql .= ' ORDER BY bc.pagenum';
+        }
 
+        $rs = $DB->get_recordset_sql($sql, $params);
         $children = array();
-        $chapters = $DB->get_records('book_chapters', array('bookid'=>$this->cm->instance), 'pagenum', 'id, pagenum');
-        foreach ($chapters as $itemid => $unused) {
-            if ($child = $this->browser->get_file_info($this->context, 'mod_book', $this->filearea, $itemid)) {
-                $children[] = $child;
+        foreach ($rs as $record) {
+            if ($child = $this->browser->get_file_info($this->context, 'mod_book', $this->filearea, $record->id)) {
+                if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
+                    $children[] = $child;
+                }
+            }
+            if ($countonly !== false && count($children) >= $countonly) {
+                break;
             }
         }
+        $rs->close();
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      * @return file_info or null for root
index 313058c..2fa245d 100644 (file)
@@ -55,12 +55,13 @@ class data_field_date extends data_field_base {
 
     //Enable the following three functions once core API issues have been addressed.
     function display_search_field($value=0) {
-        $selectors = html_writer::select_time('days', 'f_'.$this->field->id.'_d', $value)
-           . html_writer::select_time('months', 'f_'.$this->field->id.'_m', $value)
-           . html_writer::select_time('years', 'f_'.$this->field->id.'_y', $value);
-       return $selectors;
+        $selectors = html_writer::select_time('days', 'f_'.$this->field->id.'_d', $value['timestamp'])
+           . html_writer::select_time('months', 'f_'.$this->field->id.'_m', $value['timestamp'])
+           . html_writer::select_time('years', 'f_'.$this->field->id.'_y', $value['timestamp']);
+        $datecheck = html_writer::checkbox('f_'.$this->field->id.'_z', 1, $value['usedate']);
+        $str = $selectors . ' ' . $datecheck . ' ' . get_string('usedate', 'data');
 
-        //return print_date_selector('f_'.$this->field->id.'_d', 'f_'.$this->field->id.'_m', 'f_'.$this->field->id.'_y', $value, true);
+        return $str;
     }
 
     function generate_sql($tablealias, $value) {
@@ -70,21 +71,22 @@ class data_field_date extends data_field_base {
         $i++;
         $name = "df_date_$i";
         $varcharcontent = $DB->sql_compare_text("{$tablealias}.content");
-        return array(" ({$tablealias}.fieldid = {$this->field->id} AND $varcharcontent = :$name) ", array($name=>$value));
+        return array(" ({$tablealias}.fieldid = {$this->field->id} AND $varcharcontent = :$name) ", array($name => $value['timestamp']));
     }
 
     function parse_search_field() {
-
         $day   = optional_param('f_'.$this->field->id.'_d', 0, PARAM_INT);
         $month = optional_param('f_'.$this->field->id.'_m', 0, PARAM_INT);
         $year  = optional_param('f_'.$this->field->id.'_y', 0, PARAM_INT);
-        if (!empty($day) && !empty($month) && !empty($year)) {
-            return make_timestamp($year, $month, $day, 12, 0, 0, 0, false);
-        }
-        else {
+        $usedate = optional_param('f_'.$this->field->id.'_z', 0, PARAM_INT);
+        $data = array();
+        if (!empty($day) && !empty($month) && !empty($year) && $usedate == 1) {
+            $data['timestamp'] = make_timestamp($year, $month, $day, 12, 0, 0, 0, false);
+            $data['usedate'] = 1;
+            return $data;
+        } else {
             return 0;
         }
-
     }
 
     function update_content($recordid, $value, $name='') {
index ed005e6..53887ec 100644 (file)
@@ -338,6 +338,7 @@ $string['uploadrecords_help'] = 'Entries may be uploaded via text file. The form
 The field enclosure is a character that surrounds each field in each record. It can normally be left unset.';
 $string['uploadrecords_link'] = 'mod/data/import';
 $string['url'] = 'Url';
+$string['usedate'] = 'Include in search.';
 $string['usestandard'] = 'Use a preset';
 $string['usestandard_help'] = 'To use a preset available to the whole site, select it from the list. (If you have added a preset to the list using the save as preset feature then you have the option of deleting it.)';
 $string['viewfromdate'] = 'Read only from';
index e06430a..55a2070 100644 (file)
@@ -493,20 +493,79 @@ class data_file_info_container extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
+        return $this->get_filtered_children('*', false, true);
+    }
+
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
         global $DB;
+        $params = array('contextid' => $this->context->id,
+            'component' => $this->component,
+            'filearea' => $this->filearea);
+        $sql = 'SELECT DISTINCT itemid
+                    FROM {files}
+                    WHERE contextid = :contextid
+                    AND component = :component
+                    AND filearea = :filearea';
+        if (!$returnemptyfolders) {
+            $sql .= ' AND filename <> :emptyfilename';
+            $params['emptyfilename'] = '.';
+        }
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $sql .= ' '.$sql2;
+        $params = array_merge($params, $params2);
+        if ($countonly === false) {
+            $sql .= ' ORDER BY itemid DESC';
+        }
 
+        $rs = $DB->get_recordset_sql($sql, $params);
         $children = array();
-        $itemids = $DB->get_records('files', array('contextid' => $this->context->id, 'component' => $this->component,
-            'filearea' => $this->filearea), 'itemid DESC', "DISTINCT itemid");
-        foreach ($itemids as $itemid => $unused) {
-            if ($child = $this->browser->get_file_info($this->context, 'mod_data', $this->filearea, $itemid)) {
+        foreach ($rs as $record) {
+            if ($child = $this->browser->get_file_info($this->context, 'mod_data', $this->filearea, $record->itemid)) {
                 $children[] = $child;
             }
+            if ($countonly !== false && count($children) >= $countonly) {
+                break;
+            }
+        }
+        $rs->close();
+        if ($countonly !== false) {
+            return count($children);
         }
-
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
index dd1b3af..406352a 100644 (file)
@@ -484,20 +484,79 @@ class forum_file_info_container extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
+        return $this->get_filtered_children('*', false, true);
+    }
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
         global $DB;
+        $params = array('contextid' => $this->context->id,
+            'component' => $this->component,
+            'filearea' => $this->filearea);
+        $sql = 'SELECT DISTINCT itemid
+                    FROM {files}
+                    WHERE contextid = :contextid
+                    AND component = :component
+                    AND filearea = :filearea';
+        if (!$returnemptyfolders) {
+            $sql .= ' AND filename <> :emptyfilename';
+            $params['emptyfilename'] = '.';
+        }
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $sql .= ' '.$sql2;
+        $params = array_merge($params, $params2);
+        if ($countonly !== false) {
+            $sql .= ' ORDER BY itemid DESC';
+        }
 
+        $rs = $DB->get_recordset_sql($sql, $params);
         $children = array();
-        $itemids = $DB->get_records('files', array('contextid' => $this->context->id, 'component' => $this->component,
-            'filearea' => $this->filearea), 'itemid DESC', "DISTINCT itemid");
-        foreach ($itemids as $itemid => $unused) {
-            if ($child = $this->browser->get_file_info($this->context, 'mod_forum', $this->filearea, $itemid)) {
+        foreach ($rs as $record) {
+            if (($child = $this->browser->get_file_info($this->context, 'mod_forum', $this->filearea, $record->itemid))
+                    && ($returnemptyfolders || $child->count_non_empty_children($extensions))) {
                 $children[] = $child;
             }
+            if ($countonly !== false && count($children) >= $countonly) {
+                break;
+            }
+        }
+        $rs->close();
+        if ($countonly !== false) {
+            return count($children);
         }
-
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
index c569b18..5b04e57 100644 (file)
@@ -546,30 +546,86 @@ class glossary_file_info_container extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
-        global $DB;
+        return $this->get_filtered_children('*', false, true);
+    }
 
-        $sql = "SELECT DISTINCT f.itemid, ge.concept
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
+        global $DB;
+        $sql = 'SELECT DISTINCT f.itemid, ge.concept
                   FROM {files} f
-                  JOIN {modules} m ON (m.name = 'glossary' AND m.visible = 1)
-                  JOIN {course_modules} cm ON (cm.module = m.id AND cm.id = ?)
+                  JOIN {modules} m ON (m.name = :modulename AND m.visible = 1)
+                  JOIN {course_modules} cm ON (cm.module = m.id AND cm.id = :instanceid)
                   JOIN {glossary} g ON g.id = cm.instance
                   JOIN {glossary_entries} ge ON (ge.glossaryid = g.id AND ge.id = f.itemid)
-                 WHERE f.contextid = ? AND f.component = ? AND f.filearea = ?
-              ORDER BY ge.concept, f.itemid";
-        $params = array($this->context->instanceid, $this->context->id, $this->component, $this->filearea);
+                 WHERE f.contextid = :contextid
+                  AND f.component = :component
+                  AND f.filearea = :filearea';
+        $params = array(
+            'modulename' => 'glossary',
+            'instanceid' => $this->context->instanceid,
+            'contextid' => $this->context->id,
+            'component' => $this->component,
+            'filearea' => $this->filearea);
+        if (!$returnemptyfolders) {
+            $sql .= ' AND f.filename <> :emptyfilename';
+            $params['emptyfilename'] = '.';
+        }
+        list($sql2, $params2) = $this->build_search_files_sql($extensions, 'f');
+        $sql .= ' '.$sql2;
+        $params = array_merge($params, $params2);
+        if ($countonly !== false) {
+            $sql .= ' ORDER BY ge.concept, f.itemid';
+        }
 
         $rs = $DB->get_recordset_sql($sql, $params);
         $children = array();
-        foreach ($rs as $file) {
-            if ($child = $this->browser->get_file_info($this->context, 'mod_glossary', $this->filearea, $file->itemid)) {
+        foreach ($rs as $record) {
+            if ($child = $this->browser->get_file_info($this->context, 'mod_glossary', $this->filearea, $record->itemid)) {
                 $children[] = $child;
             }
+            if ($countonly !== false && count($children) >= $countonly) {
+                break;
+            }
         }
         $rs->close();
-
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      *
index 6eb6ebb..04b16fb 100644 (file)
@@ -273,18 +273,79 @@ class imscp_file_info extends file_info {
      * @return array of file_info instances
      */
     public function get_children() {
+        return $this->get_filtered_children('*', false, true);
+    }
+
+    /**
+     * Help function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
         global $DB;
+        $params = array('contextid' => $this->context->id,
+            'component' => 'mod_imscp',
+            'filearea' => $this->filearea);
+        $sql = 'SELECT DISTINCT itemid
+                    FROM {files}
+                    WHERE contextid = :contextid
+                    AND component = :component
+                    AND filearea = :filearea';
+        if (!$returnemptyfolders) {
+            $sql .= ' AND filename <> :emptyfilename';
+            $params['emptyfilename'] = '.';
+        }
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $sql .= ' '.$sql2;
+        $params = array_merge($params, $params2);
+        if ($countonly !== false) {
+            $sql .= ' ORDER BY itemid';
+        }
 
+        $rs = $DB->get_recordset_sql($sql, $params);
         $children = array();
-        $itemids = $DB->get_records('files', array('contextid'=>$this->context->id, 'component'=>'mod_imscp', 'filearea'=>$this->filearea), 'itemid', "DISTINCT itemid");
-        foreach ($itemids as $itemid=>$unused) {
-            if ($child = $this->browser->get_file_info($this->context, 'mod_imscp', $this->filearea, $itemid)) {
+        foreach ($rs as $record) {
+            if ($child = $this->browser->get_file_info($this->context, 'mod_imscp', $this->filearea, $record->itemid)) {
                 $children[] = $child;
+                if ($countonly !== false && count($children) >= $countonly) {
+                    break;
+                }
             }
         }
+        $rs->close();
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      * @return file_info or null for root
diff --git a/mod/lesson/importppt.php b/mod/lesson/importppt.php
deleted file mode 100644 (file)
index 84bd61b..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-<?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 is a very rough importer for powerpoint slides
- * Export a powerpoint presentation with powerpoint as html pages
- * Do it with office 2002 (I think?) and no special settings
- * Then zip the directory with all of the html pages
- * and the zip file is what you want to upload
- *
- * The script supports book and lesson.
- *
- * @package    mod
- * @subpackage lesson
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- **/
-
-/** include required files */
-require_once("../../config.php");
-require_once($CFG->dirroot.'/mod/lesson/locallib.php');
-require_once($CFG->dirroot.'/mod/lesson/importpptlib.php');
-
-$id     = required_param('id', PARAM_INT);         // Course Module ID
-$pageid = optional_param('pageid', '', PARAM_INT); // Page ID
-
-$url = new moodle_url('/mod/lesson/importppt.php', array('id'=>$id));
-if ($pageid !== '') {
-    $url->param('pageid', $pageid);
-}
-$PAGE->set_url($url);
-
-$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);;
-$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
-$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
-
-$modname = 'lesson';
-$mod = $cm;
-require_login($course, false, $cm);
-
-require_login($course, false, $cm);
-$context = get_context_instance(CONTEXT_MODULE, $cm->id);
-require_capability('mod/lesson:edit', $context);
-
-$strimportppt = get_string("importppt", "lesson");
-$strlessons = get_string("modulenameplural", "lesson");
-
-$data = new stdClass;
-$data->id = $cm->id;
-$data->pageid = $pageid;
-$mform = new lesson_importppt_form();
-$mform->set_data($data);
-
-if ($data = $mform->get_data()) {
-    $manager = lesson_page_type_manager::get($lesson);
-    if (!$filename = $mform->get_new_filename('pptzip')) {
-        print_error('invalidfile', 'lesson');
-    }
-    if (!$package = $mform->save_stored_file('pptzip', $context->id, 'mod_lesson', 'ppt_imports', $lesson->id, '/', $filename, true)) {
-        print_error('unabletosavefile', 'lesson');
-    }
-    // extract package content
-    $packer = get_file_packer('application/zip');
-    $package->extract_to_storage($packer, $context->id, 'mod_lesson', 'imported_files', $lesson->id, '/');
-
-    $fs = get_file_storage();
-    if ($files = $fs->get_area_files($context->id, 'mod_lesson', 'imported_files', $lesson->id)) {
-
-        $pages = array();
-        foreach ($files as $key=>$file) {
-            if ($file->get_mimetype() != 'text/html') {
-                continue;
-            }
-            $filenameinfo = pathinfo($file->get_filepath().$file->get_filename());
-
-            $page = new stdClass;
-            $page->title = '';
-            $page->contents = array();
-            $page->images = array();
-            $page->source = $filenameinfo['basename'];
-
-            $string = strip_tags($file->get_content(),'<div><img>');
-            $imgs = array();
-            preg_match_all("/<img[^>]*(src\=\"(".$filenameinfo['filename']."\_image[^>^\"]*)\"[^>]*)>/i", $string, $imgs);
-            foreach ($imgs[2] as $img) {
-                $imagename = basename($img);
-                foreach ($files as $file) {
-                    if ($imagename === $file->get_filename()) {
-                        $page->images[] = clone($file);
-                    }
-                }
-            }
-
-            $matches = array();
-            // this will look for a non nested tag that is closed
-            // want to allow <b><i>(maybe more) tags but when we do that
-            // the preg_match messes up.
-            preg_match_all("/(<([\w]+)[^>]*>)([^<\\2>]*)(<\/\\2>)/", $string, $matches);
-            $countmatches = count($matches[1]);
-            for($i = 0; $i < $countmatches; $i++) { // go through all of our div matches
-
-                $class = lesson_importppt_isolate_class($matches[1][$i]); // first step in isolating the class
-
-                // check for any static classes
-                switch ($class) {
-                    case 'T':  // class T is used for Titles
-                        $page->title = $matches[3][$i];
-                        break;
-                    case 'B':  // I would guess that all bullet lists would start with B then go to B1, B2, etc
-                    case 'B1': // B1-B4 are just insurance, should just hit B and all be taken care of
-                    case 'B2':
-                    case 'B3':
-                    case 'B4':
-                        $page->contents[] = lesson_importppt_build_list($matches, '<ul>', $i, 0);  // this is a recursive function that will grab all the bullets and rebuild the list in html
-                        break;
-                    default:
-                        if ($matches[3][$i] != '&#13;') {  // odd crap generated... sigh
-                            if (substr($matches[3][$i], 0, 1) == ':') {  // check for leading :    ... hate MS ...
-                                $page->contents[] = substr($matches[3][$i], 1);  // get rid of :
-                            } else {
-                                $page->contents[] = $matches[3][$i];
-                            }
-                        }
-                        break;
-                }
-            }
-            $pages[] = $page;
-        }
-
-        $branchtables = lesson_create_objects($pages, $lesson->id);
-
-        // first set up the prevpageid and nextpageid
-        if (empty($pageid)) { // adding it to the top of the lesson
-            $prevpageid = 0;
-            // get the id of the first page.  If not found, then no pages in the lesson
-            if (!$nextpageid = $DB->get_field('lesson_pages', 'id', array('prevpageid' => 0, 'lessonid' => $lesson->id))) {
-                $nextpageid = 0;
-            }
-        } else {
-            // going after an actual page
-            $prevpageid = $pageid;
-            $nextpageid = $DB->get_field('lesson_pages', 'nextpageid', array('id' => $pageid));
-        }
-
-        foreach ($branchtables as $branchtable) {
-
-            // set the doubly linked list
-            $branchtable->page->nextpageid = $nextpageid;
-            $branchtable->page->prevpageid = $prevpageid;
-
-            // insert the page
-            $id = $DB->insert_record('lesson_pages', $branchtable->page);
-
-            if (!empty($branchtable->page->images)) {
-                $changes = array('contextid'=>$context->id, 'component'=>'mod_lesson', 'filearea'=>'page_contents', 'itemid'=>$id, 'timemodified'=>time());
-                foreach ($branchtable->page->images as $image) {
-                    $fs->create_file_from_storedfile($changes, $image);
-                }
-            }
-
-            // update the link of the page previous to the one we just updated
-            if ($prevpageid != 0) {  // if not the first page
-                $DB->set_field("lesson_pages", "nextpageid", $id, array("id" => $prevpageid));
-            }
-
-            // insert the answers
-            foreach ($branchtable->answers as $answer) {
-                $answer->pageid = $id;
-                $DB->insert_record('lesson_answers', $answer);
-            }
-
-            $prevpageid = $id;
-        }
-
-        // all done with inserts.  Now check to update our last page (this is when we import between two lesson pages)
-        if ($nextpageid != 0) {  // if the next page is not the end of lesson
-            $DB->set_field("lesson_pages", "prevpageid", $id, array("id" => $nextpageid));
-        }
-    }
-
-    // Remove all unzipped files!
-    $fs->delete_area_files($context->id, 'mod_lesson', 'imported_files', $lesson->id);
-
-    redirect("$CFG->wwwroot/mod/$modname/view.php?id=$cm->id", get_string('pptsuccessfullimport', 'lesson'), 5);
-}
-
-$PAGE->navbar->add($strimportppt);
-$PAGE->set_title($strimportppt);
-$PAGE->set_heading($strimportppt);
-echo $OUTPUT->header();
-
-/// Print upload form
-echo $OUTPUT->heading_with_help($strimportppt, 'importppt', 'lesson');
-echo $OUTPUT->box_start('generalbox boxaligncenter');
-$mform->display();
-echo $OUTPUT->box_end();
-echo $OUTPUT->footer();
diff --git a/mod/lesson/importpptlib.php b/mod/lesson/importpptlib.php
deleted file mode 100644 (file)
index 8550e61..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-<?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/>.
-
-/**
- * Contains functions used by importppt.php that naturally pertain to importing
- * powerpoint presentations into the lesson module
- *
- * @package    mod
- * @subpackage lesson
- * @copyright  2009 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- **/
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * A recursive function to build a html list
- *
- * @param array $matches
- * @param string $list
- * @param int $i
- * @param int $depth
- * @return string
- */
-function lesson_importppt_build_list(array &$matches, $list, &$i, $depth) {
-    while($i < count($matches[1])) {
-
-        $class = lesson_importppt_isolate_class($matches[1][$i]);
-
-        if (strstr($class, 'B')) {  // make sure we are still working with bullet classes
-            if ($class == 'B') {
-                $this_depth = 0;  // calling class B depth 0
-            } else {
-                // set the depth number.  So B1 is depth 1 and B2 is depth 2 and so on
-                $this_depth = substr($class, 1);
-                if (!is_numeric($this_depth)) {
-                    print_error('invalidnum');
-                }
-            }
-            if ($this_depth < $depth) {
-                // we are moving back a level in the nesting
-                break;
-            }
-            if ($this_depth > $depth) {
-                // we are moving in a lvl in nesting
-                $list .= '<ul>';
-                $list = lesson_importppt_build_list($matches, $list, $i, $this_depth);
-                // once we return back, should go to the start of the while
-                continue;
-            }
-            // no depth changes, so add the match to our list
-            if ($cleanstring = lesson_importppt_clean_text($matches[3][$i])) {
-                $list .= '<li>'.lesson_importppt_clean_text($matches[3][$i]).'</li>';
-            }
-            $i++;
-        } else {
-            // not a B class, so get out of here...
-            break;
-        }
-    }
-    // end the list and return it
-    $list .= '</ul>';
-    return $list;
-
-}
-
-/**
- * Given an html tag, this function will
- *
- * @param string $string
- * @return string
- */
-function lesson_importppt_isolate_class($string) {
-    if($class = strstr($string, 'class=')) { // first step in isolating the class
-        $class = substr($class, strpos($class, '=')+1);  // this gets rid of <div blawblaw class=  there are no "" or '' around the class name   ...sigh...
-        if (strstr($class, ' ')) {
-            // spaces found, so cut off everything off after the first space
-            return substr($class, 0, strpos($class, ' '));
-        } else {
-            // no spaces so nothing else in the div tag, cut off the >
-            return substr($class, 0, strpos($class, '>'));
-        }
-    } else {
-        // no class defined in the tag
-        return '';
-    }
-}
-
-/**
- * This function strips off the random chars that ppt puts infront of bullet lists
- *
- * @param string $string
- * @return string
- */
-function lesson_importppt_clean_text($string) {
-    $chop = 1; // default: just a single char infront of the content
-
-    // look for any other crazy things that may be infront of the content
-    if (strstr($string, '&lt;') and strpos($string, '&lt;') == 0) {  // look for the &lt; in the sting and make sure it is in the front
-        $chop = 4;  // increase the $chop
-    }
-    // may need to add more later....
-
-    $string = substr($string, $chop);
-
-    if ($string != '&#13;') {
-        return $string;
-    } else {
-        return false;
-    }
-}
-
-/**
- *  Creates objects an object with the page and answers that are to be inserted into the database
- *
- * @param array $pageobjects
- * @param int $lessonid
- * @return array
- */
-function lesson_create_objects($pageobjects, $lessonid) {
-
-    $branchtables = array();
-    $branchtable = new stdClass;
-
-    // all pages have this info
-    $page = new stdClass();
-    $page->lessonid = $lessonid;
-    $page->prevpageid = 0;
-    $page->nextpageid = 0;
-    $page->qtype = LESSON_PAGE_BRANCHTABLE;
-    $page->qoption = 0;
-    $page->layout = 1;
-    $page->display = 1;
-    $page->timecreated = time();
-    $page->timemodified = 0;
-
-    // all answers are the same
-    $answer = new stdClass();
-    $answer->lessonid = $lessonid;
-    $answer->jumpto = LESSON_NEXTPAGE;
-    $answer->grade = 0;
-    $answer->score = 0;
-    $answer->flags = 0;
-    $answer->timecreated = time();
-    $answer->timemodified = 0;
-    $answer->answer = "Next";
-    $answer->response = "";
-
-    $answers[] = clone($answer);
-
-    $answer->jumpto = LESSON_PREVIOUSPAGE;
-    $answer->answer = "Previous";
-
-    $answers[] = clone($answer);
-
-    $branchtable->answers = $answers;
-
-    $i = 1;
-
-    foreach ($pageobjects as $pageobject) {
-        if ($pageobject->title == '') {
-            $page->title = "Page $i";  // no title set so make a generic one
-        } else {
-            $page->title = $pageobject->title;
-        }
-        $page->contents = '';
-
-        // nab all the images first
-        $page->images = $pageobject->images;
-        foreach ($page->images as $image) {
-            $imagetag = '<img src="@@PLUGINFILE@@'.$image->get_filepath().$image->get_filename().'" title="'.$image->get_filename().'" />';
-            $imagetag = str_replace("\n", '', $imagetag);
-            $imagetag = str_replace("\r", '', $imagetag);
-            $imagetag = str_replace("'", '"', $imagetag);  // imgstyle
-            $page->contents .= $imagetag;
-        }
-        // go through the contents array and put <p> tags around each element and strip out \n which I have found to be unneccessary
-        foreach ($pageobject->contents as $content) {
-            $content = str_replace("\n", '', $content);
-            $content = str_replace("\r", '', $content);
-            $content = str_replace('&#13;', '', $content);  // puts in returns?
-            $content = '<p>'.$content.'</p>';
-            $page->contents .= $content;
-        }
-
-        $branchtable->page = clone($page);  // add the page
-        $branchtables[] = clone($branchtable);  // add it all to our array
-        $i++;
-    }
-
-    return $branchtables;
-}
-
-/**
- * Form displayed to the user asking them to select a file to upload
- *
- * @copyright  2009 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class lesson_importppt_form extends moodleform {
-
-    public function definition() {
-        global $COURSE;
-
-        $mform = $this->_form;
-
-        $mform->addElement('hidden', 'id');
-        $mform->setType('id', PARAM_INT);
-
-        $mform->addElement('hidden', 'pageid');
-        $mform->setType('pageid', PARAM_INT);
-
-        $filepickeroptions = array();
-        $filepickeroptions['filetypes'] = array('*.zip');
-        $filepickeroptions['maxbytes'] = $COURSE->maxbytes;
-        $mform->addElement('filepicker', 'pptzip', get_string('upload'), null, $filepickeroptions);
-        $mform->addRule('pptzip', null, 'required', null, 'client');
-
-        $this->add_action_buttons(null, get_string("uploadthisfile"));
-    }
-
-}
\ No newline at end of file
index 765b7ca..a37f40b 100644 (file)
@@ -364,9 +364,6 @@ class mod_lesson_renderer extends plugin_renderer_base {
         $importquestionsurl = new moodle_url('/mod/lesson/import.php',array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
         $links[] = html_writer::link($importquestionsurl, get_string('importquestions', 'lesson'));
 
-        $importppturl = new moodle_url('/mod/lesson/importppt.php',array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
-        $links[] = html_writer::link($importppturl, get_string('importppt', 'lesson'));
-
         $manager = lesson_page_type_manager::get($lesson);
         foreach ($manager->get_add_page_type_links($prevpageid) as $link) {
             $link['addurl']->param('firstpage', 1);
index e663af6..d75f2aa 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->version   = 2012061700;       // The current module version (Date: YYYYMMDDXX)
+$module->version   = 2012061701;       // The current module version (Date: YYYYMMDDXX)
 $module->requires  = 2012061700;    // Requires this Moodle version
 $module->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics)
 $module->cron      = 0;
index 5a8b4a2..0427819 100644 (file)
@@ -87,24 +87,90 @@ class workshop_file_info_submissions_container extends file_info {
         return true;
     }
 
+
     /**
-     * Returns list of children.
+     * Returns list of children nodes
+     *
      * @return array of file_info instances
      */
     public function get_children() {
+        return $this->get_filtered_children('*', false, true);
+    }
+
+    /**
+     * Helper function to return files matching extensions or their count
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @param bool|int $countonly if false returns the children, if an int returns just the
+     *    count of children but stops counting when $countonly number of children is reached
+     * @param bool $returnemptyfolders if true returns items that don't have matching files inside
+     * @return array|int array of file_info instances or the count
+     */
+    private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
         global $DB;
+        $params = array('contextid' => $this->context->id,
+            'component' => 'mod_workshop',
+            'filearea' => $this->filearea);
+        $sql = 'SELECT DISTINCT itemid
+                    FROM {files}
+                    WHERE contextid = :contextid
+                    AND component = :component
+                    AND filearea = :filearea';
+        if (!$returnemptyfolders) {
+            $sql .= ' AND filename <> :emptyfilename';
+            $params['emptyfilename'] = '.';
+        }
+        list($sql2, $params2) = $this->build_search_files_sql($extensions);
+        $sql .= ' '.$sql2;
+        $params = array_merge($params, $params2);
+        if ($countonly !== false) {
+            $sql .= ' ORDER BY itemid DESC';
+        }
 
+        $rs = $DB->get_recordset_sql($sql, $params);
         $children = array();
-        $itemids = $DB->get_records('files', array('contextid' => $this->context->id, 'component' => 'mod_workshop', 'filearea' => $this->filearea),
-            'itemid', "DISTINCT itemid");
-        foreach ($itemids as $itemid => $unused) {
-            if ($child = $this->browser->get_file_info($this->context, 'mod_workshop', $this->filearea, $itemid)) {
+        foreach ($rs as $record) {
+            if (($child = $this->browser->get_file_info($this->context, 'mod_workshop', $this->filearea, $record->itemid))
+                    && ($returnemptyfolders || $child->count_non_empty_children($extensions))) {
                 $children[] = $child;
+                if ($countonly !== false && count($children) >= $countonly) {
+                    break;
+                }
             }
         }
+        $rs->close();
+        if ($countonly !== false) {
+            return count($children);
+        }
         return $children;
     }
 
+    /**
+     * Returns list of children which are either files matching the specified extensions
+     * or folders that contain at least one such file.
+     *
+     * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
+     * @return array of file_info instances
+     */
+    public function get_non_empty_children($extensions = '*') {
+        return $this->get_filtered_children($extensions, false);
+    }
+
+    /**
+     * Returns the number of children which are either files matching the specified extensions
+     * or folders containing at least one such file.
+     *
+     * NOTE: We don't need the exact number of non empty children if it is >=2
+     * In this function 1 is never returned to avoid skipping the single subfolder
+     *
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param int $limit stop counting after at least $limit non-empty children are found
+     * @return int
+     */
+    public function count_non_empty_children($extensions = '*', $limit = 1) {
+        return $this->get_filtered_children($extensions, $limit);
+    }
+
     /**
      * Returns parent file_info instance
      * @return file_info or null for root
index 9654045..5e848cf 100644 (file)
@@ -16,6 +16,7 @@
  * this.viewmode, store current view mode
  * this.pathbar, reference to the Node with path bar
  * this.pathnode, a Node element representing one folder in a path bar (not attached anywhere, just used for template)
+ * this.currentpath, the current path in the repository (or last requested path)
  *
  * Filepicker options:
  * =====
@@ -39,7 +40,7 @@
  * this.filelist, cached filelist
  * this.pages
  * this.page
- * this.filepath, current path
+ * this.filepath, current path (each element of the array is a part of the breadcrumb)
  * this.logindata, cached login form
  */
 
@@ -932,6 +933,7 @@ M.core_filepicker.init = function(Y, options) {
                         // save current path and filelist (in case we want to jump to other viewmode)
                         this.filepath = e.node.origpath;
                         this.filelist = e.node.origlist;
+                        this.currentpath = e.node.path;
                         this.print_path();
                         this.content_scrolled();
                     }
@@ -967,7 +969,6 @@ M.core_filepicker.init = function(Y, options) {
                         if (this.active_repo.dynload) {
                             this.list({'path':node.path});
                         } else {
-                            this.filepath = node.path;
                             this.filelist = node.children;
                             this.view_files();
                         }
@@ -1002,7 +1003,6 @@ M.core_filepicker.init = function(Y, options) {
                         if (this.active_repo.dynload) {
                             this.list({'path':node.path});
                         } else {
-                            this.filepath = node.path;
                             this.filelist = node.children;
                             this.view_files();
                         }
@@ -1022,9 +1022,13 @@ M.core_filepicker.init = function(Y, options) {
             }
             this.active_repo.nextpagerequested = true;
             var nextpage = this.active_repo.page+1;
-            var args = {page:nextpage, repo_id:this.active_repo.id, path:this.active_repo.path};
+            var args = {
+                page: nextpage,
+                repo_id: this.active_repo.id,
+            };
             var action = this.active_repo.issearchresult ? 'search' : 'list';
             this.request({
+                path: this.currentpath,
                 scope: this,
                 action: action,
                 client_id: this.options.client_id,
@@ -1032,10 +1036,20 @@ M.core_filepicker.init = function(Y, options) {
                 params: args,
                 callback: function(id, obj, args) {
                     var scope = args.scope;
-                    // check that we are still in the same repository and are expecting this page
+                    // Check that we are still in the same repository and are expecting this page. We have no way
+                    // to compare the requested page and the one returned, so we assume that if the last chunk
+                    // of the breadcrumb is similar, then we probably are on the same page.
+                    var samepage = true;
+                    if (obj.path && scope.filepath) {
+                        var pathbefore = scope.filepath[scope.filepath.length-1];
+                        var pathafter = obj.path[obj.path.length-1];
+                        if (pathbefore.path != pathafter.path) {
+                            samepage = false;
+                        }
+                    }
                     if (scope.active_repo.hasmorepages && obj.list && obj.page &&
                             obj.repo_id == scope.active_repo.id &&
-                            obj.page == scope.active_repo.page+1 && obj.path == scope.path) {
+                            obj.page == scope.active_repo.page+1 && samepage) {
                         scope.parse_repository_options(obj, true);
                         scope.view_files(obj.list)
                     }
@@ -1564,6 +1578,10 @@ M.core_filepicker.init = function(Y, options) {
             if (!args.repo_id) {
                 args.repo_id = this.active_repo.id;
             }
+            if (!args.path) {
+                args.path = '';
+            }
+            this.currentpath = args.path;
             this.request({
                 action: 'list',
                 client_id: this.options.client_id,
@@ -1707,7 +1725,7 @@ M.core_filepicker.init = function(Y, options) {
             toolbar.one('.fp-tb-refresh').one('a,button').on('click', function(e) {
                 e.preventDefault();
                 if (!this.active_repo.norefresh) {
-                    this.list();
+                    this.list({ path: this.currentpath });
                 }
             }, this);
             toolbar.one('.fp-tb-search form').
index b416284..f60bed4 100644 (file)
@@ -2018,59 +2018,56 @@ abstract class repository {
     public function get_listing($path = '', $page = '') {
     }
 
+
     /**
-     * Prepares list of files before passing it to AJAX, makes sure data is in the correct
-     * format and stores formatted values.
+     * Prepare the breadcrumb.
      *
-     * @param array|stdClass $listing result of get_listing() or search() or file_get_drafarea_files()
-     * @return array
+     * @param array $breadcrumb contains each element of the breadcrumb.
+     * @return array of breadcrumb elements.
+     * @since 2.3.3
      */
-    public static function prepare_listing($listing) {
+    protected static function prepare_breadcrumb($breadcrumb) {
         global $OUTPUT;
-
-        $defaultfoldericon = $OUTPUT->pix_url(file_folder_icon(24))->out(false);
-        // prepare $listing['path'] or $listing->path
-        if (is_array($listing) && isset($listing['path']) && is_array($listing['path'])) {
-            $path = &$listing['path'];
-        } else if (is_object($listing) && isset($listing->path) && is_array($listing->path)) {
-            $path = &$listing->path;
-        }
-        if (isset($path)) {
-            $len = count($path);
-            for ($i=0; $i<$len; $i++) {
-                if (is_array($path[$i]) && !isset($path[$i]['icon'])) {
-                    $path[$i]['icon'] = $defaultfoldericon;
-                } else if (is_object($path[$i]) && !isset($path[$i]->icon)) {
-                    $path[$i]->icon = $defaultfoldericon;
-                }
+        $foldericon = $OUTPUT->pix_url(file_folder_icon(24))->out(false);
+        $len = count($breadcrumb);
+        for ($i = 0; $i < $len; $i++) {
+            if (is_array($breadcrumb[$i]) && !isset($breadcrumb[$i]['icon'])) {
+                $breadcrumb[$i]['icon'] = $foldericon;
+            } else if (is_object($breadcrumb[$i]) && !isset($breadcrumb[$i]->icon)) {
+                $breadcrumb[$i]->icon = $foldericon;
             }
         }
+        return $breadcrumb;
+    }
 
-        // prepare $listing['list'] or $listing->list
-        if (is_array($listing) && isset($listing['list']) && is_array($listing['list'])) {
-            $listing['list'] = array_values($listing['list']); // convert to array
-            $files = &$listing['list'];
-        } else if (is_object($listing) && isset($listing->list) && is_array($listing->list)) {
-            $listing->list = array_values($listing->list); // convert to array
-            $files = &$listing->list;
-        } else {
-            return $listing;
-        }
-        $len = count($files);
-        for ($i=0; $i<$len; $i++) {
-            if (is_object($files[$i])) {
-                $file = (array)$files[$i];
+    /**
+     * Prepare the file/folder listing.
+     *
+     * @param array $list of files and folders.
+     * @return array of files and folders.
+     * @since 2.3.3
+     */
+    protected static function prepare_list($list) {
+        global $OUTPUT;
+        $foldericon = $OUTPUT->pix_url(file_folder_icon(24))->out(false);
+
+        // Reset the array keys because non-numeric keys will create an object when converted to JSON.
+        $list = array_values($list);
+
+        $len = count($list);
+        for ($i = 0; $i < $len; $i++) {
+            if (is_object($list[$i])) {
+                $file = (array)$list[$i];
                 $converttoobject = true;
             } else {
-                $file = & $files[$i];
+                $file =& $list[$i];
                 $converttoobject = false;
             }
             if (isset($file['size'])) {
                 $file['size'] = (int)$file['size'];
                 $file['size_f'] = display_size($file['size']);
             }
-            if (isset($file['license']) &&
-                    get_string_manager()->string_exists($file['license'], 'license')) {
+            if (isset($file['license']) && get_string_manager()->string_exists($file['license'], 'license')) {
                 $file['license_f'] = get_string($file['license'], 'license');
             }
             if (isset($file['image_width']) && isset($file['image_height'])) {
@@ -2105,15 +2102,53 @@ abstract class repository {
             }
             if (!isset($file['icon'])) {
                 if ($isfolder) {
-                    $file['icon'] = $defaultfoldericon;
+                    $file['icon'] = $foldericon;
                 } else if ($filename) {
                     $file['icon'] = $OUTPUT->pix_url(file_extension_icon($filename, 24))->out(false);
                 }
             }
+
+            // Recursively loop over children.
+            if (isset($file['children'])) {
+                $file['children'] = self::prepare_list($file['children']);
+            }
+
+            // Convert the array back to an object.
             if ($converttoobject) {
-                $files[$i] = (object)$file;
+                $list[$i] = (object)$file;
             }
         }
+        return $list;
+    }
+
+    /**
+     * Prepares list of files before passing it to AJAX, makes sure data is in the correct
+     * format and stores formatted values.
+     *
+     * @param array|stdClass $listing result of get_listing() or search() or file_get_drafarea_files()
+     * @return array
+     */
+    public static function prepare_listing($listing) {
+        $wasobject = false;
+        if (is_object($listing)) {
+            $listing = (array) $listing;
+            $wasobject = true;
+        }
+
+        // Prepare the breadcrumb, passed as 'path'.
+        if (isset($listing['path']) && is_array($listing['path'])) {
+            $listing['path'] = self::prepare_breadcrumb($listing['path']);
+        }
+
+        // Prepare the listing of objects.
+        if (isset($listing['list']) && is_array($listing['list'])) {
+            $listing['list'] = self::prepare_list($listing['list']);
+        }
+
+        // Convert back to an object.
+        if ($wasobject) {
+            $listing = (object) $listing;
+        }
         return $listing;
     }
 
index d8898bf..b41100c 100644 (file)
@@ -29,22 +29,16 @@ require_once($CFG->dirroot . '/repository/lib.php');
  *
  * @since 2.0
  * @package    repository_local
+ * @copyright  2012 Marina Glancy
  * @copyright  2009 Dongsheng Cai {@link http://dongsheng.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class repository_local extends repository {
-    /**
-     * local plugin doesn't require login, so list all files
-     * @return mixed
-     */
-    public function print_login() {
-        return $this->get_listing();
-    }
-
     /**
      * Get file listing
      *
      * @param string $encodedpath
+     * @param string $page no paging is used in repository_local
      * @return mixed
      */
     public function get_listing($encodedpath = '', $page = '') {
@@ -53,11 +47,17 @@ class repository_local extends repository {
         $ret['dynload'] = true;
         $ret['nosearch'] = true;
         $ret['nologin'] = true;
-        $list = array();
+        $ret['list'] = array();
+
+        $itemid   = null;
+        $filename = null;
+        $filearea = null;
+        $filepath = null;
+        $component = null;
 
         if (!empty($encodedpath)) {
             $params = unserialize(base64_decode($encodedpath));
-            if (is_array($params)) {
+            if (is_array($params) && isset($params['contextid'])) {
                 $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT);
                 $filearea  = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA);
                 $itemid    = is_null($params['itemid']) ? NULL : clean_param($params['itemid'], PARAM_INT);
@@ -65,52 +65,51 @@ class repository_local extends repository {
                 $filename  = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE);
                 $context = get_context_instance_by_id(clean_param($params['contextid'], PARAM_INT));
             }
+        }
+        if (empty($context) && !empty($this->context)) {
+            $context = $this->context->get_course_context(false);
+        }
+        if (empty($context)) {
+            $context = context_system::instance();
+        }
+
+        // prepare list of allowed extensions: $extensions is either string '*'
+        // or array of lowercase extensions, i.e. array('.gif','.jpg')
+        $extensions = optional_param_array('accepted_types', '', PARAM_RAW);
+        if (empty($extensions) || $extensions === '*' || (is_array($extensions) && in_array('*', $extensions))) {
+            $extensions = '*';
         } else {
-            $itemid   = null;
-            $filename = null;
-            $filearea = null;
-            $filepath = null;
-            $component = null;
-            if (!empty($this->context)) {
-                list($context, $course, $cm) = get_context_info_array($this->context->id);
-                if (is_object($course)) {
-                    $context = get_context_instance(CONTEXT_COURSE, $course->id);
-                } else {
-                    $context = get_system_context();
-                }
-            } else {
-                $context = get_system_context();
+            if (!is_array($extensions)) {
+                $extensions = array($extensions);
             }
+            $extensions = array_map('textlib::strtolower', $extensions);
         }
 
+        // build file tree
         $browser = get_file_browser();
-
-        $list = array();
-        if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename)) {
-            // build file tree
-            $element = repository_local_file::retrieve_file_info($fileinfo, $this);
-            $nonemptychildren = $element->get_non_empty_children();
-            foreach ($nonemptychildren as $child) {
-                $list[] = (array)$child->get_node();
-            }
-        } else {
+        if (!($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename))) {
             // if file doesn't exist, build path nodes root of current context
             $fileinfo = $browser->get_file_info($context, null, null, null, null, null);
         }
+        $ret['list'] = $this->get_non_empty_children($fileinfo, $extensions);
+
         // build path navigation
+        $path = array();
+        for ($level = $fileinfo; $level; $level = $level->get_parent()) {
+            array_unshift($path, $level);
+        }
+        array_unshift($path, null);
         $ret['path'] = array();
-        $element = repository_local_file::retrieve_file_info($fileinfo, $this);
-        for ($level = $element; $level; $level = $level->get_parent()) {
-            if ($level == $element || !$level->can_skip()) {
-                array_unshift($ret['path'], $level->get_node_path());
+        for ($i=1; $i<count($path); $i++) {
+            if ($path[$i] == $fileinfo || !$this->can_skip($path[$i], $extensions, $path[$i-1])) {
+                $ret['path'][] = $this->get_node_path($path[$i]);
             }
         }
-        $ret['list'] = array_filter($list, array($this, 'filter'));
         return $ret;
     }
 
     /**
-     * Local file don't support to link to external links
+     * Tells how the file can be picked from this repository
      *
      * @return int
      */
@@ -137,111 +136,112 @@ class repository_local extends repository {
         // this should be realtime
         return 0;
     }
-}
-
-/**
- * Class to cache some information about file
- *
- * This class is a wrapper to instances of file_info. It caches such information as
- * parent and list of children. It also stores an array of already retrieved elements.
- *
- * It also implements more comprehensive algorithm for checking if folder is empty
- * (taking into account the filtering of the files). To decrease number of levels
- * we check if some subfolders can be skipped from the tree.
- *
- * As a result we display in Server files repository only non-empty folders and skip
- * filearea folders if this is the only filearea in the module.
- * For non-admin the course categories are not shown as well (courses are shown as a list)
- *
- * @package    repository_local
- * @copyright  2012 Marina Glancy
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class repository_local_file {
-    /** @var array stores already retrieved files */
-    private static $cachedfiles = array();
-    /** @var file_info Stores the original file */
-    public $fileinfo;
-    /** @var bool whether this file is directory */
-    private $isdir;
-    /** @var array caches retrieved children */
-    private $children = null;
-    /** @var array caches retrieved information whether this file is an empty directory */
-    protected $isempty = null;
-    /** @var repository link to the container repository (for filtering the results) */
-    private $repository;
-    /** @var repository_local_file link to parent directory */
-    protected $parent;
-    /** @var bool caches calculated information on whether this directory must be skipped in the tree */
-    private $skip = null;
 
     /**
-     * Creates (or retrieves from cache) the repository_local_file object for $file_info
+     * Returns all children elements that have one of the specified extensions
+     *
+     * This function may skip subfolders and recursively add their children
+     * {@link repository_local::can_skip()}
      *
      * @param file_info $fileinfo
-     * @param repository $repository
-     * @param repository_local_file $parent
-     * @return repository_local_file
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @return array array of file_info elements
      */
-    public static function retrieve_file_info(file_info $fileinfo, repository $repository, repository_local_file $parent = null) {
-        $encodedpath = base64_encode(serialize($fileinfo->get_params()));
-        if (!isset(self::$cachedfiles[$encodedpath])) {
-            self::$cachedfiles[$encodedpath] = new repository_local_file($fileinfo, $repository, $parent);
+    private function get_non_empty_children(file_info $fileinfo, $extensions) {
+        $nonemptychildren = $fileinfo->get_non_empty_children($extensions);
+        $list = array();
+        foreach ($nonemptychildren as $child) {
+            if ($this->can_skip($child, $extensions, $fileinfo)) {
+                $list = array_merge($list, $this->get_non_empty_children($child, $extensions));
+            } else {
+                $list[] = $this->get_node($child);
+            }
         }
-        return self::$cachedfiles[$encodedpath];
+        return $list;
     }
 
     /**
-     * Creates an object
+     * Whether this folder may be skipped in folder hierarchy
+     *
+     * 1. Skip the name of a single filearea in a module
+     * 2. Skip course categories for non-admins who do not have navshowmycoursecategories setting
      *
      * @param file_info $fileinfo
-     * @param repository $repository
-     * @param repository_local_file $parent
+     * @param string|array $extensions, for example '*' or array('.gif','.jpg')
+     * @param file_info|int $parent specify parent here if we know it to avoid creating extra objects
+     * @return bool
      */
-    private function __construct(file_info $fileinfo, repository $repository, repository_local_file $parent = null) {
-        $this->repository = $repository;
-        $this->fileinfo = $fileinfo;
-        $this->isdir = $fileinfo->is_directory();
-        if (!$this->isdir) {
-            $node = array('title' => $this->fileinfo->get_visible_name());
-            $this->isempty = !$repository->filter($node);
-            $this->skip = false;
+    private function can_skip(file_info $fileinfo, $extensions, $parent = -1) {
+        global $CFG;
+        if (!$fileinfo->is_directory()) {
+            // do not skip files
+            return false;
         }
+        if ($fileinfo instanceof file_info_context_coursecat) {
+            // This is a course category. For non-admins we do not display categories
+            return empty($CFG->navshowmycoursecategories) &&
+                            !has_capability('moodle/course:update', context_system::instance());
+        } else if ($fileinfo instanceof file_info_context_course ||
+                $fileinfo instanceof file_info_context_user ||
+                $fileinfo instanceof file_info_area_course_legacy ||
+                $fileinfo instanceof file_info_context_module ||
+                $fileinfo instanceof file_info_context_system) {
+            // these instances can never be filearea inside an activity, they will never be skipped
+            return false;
+        } else {
+            $params = $fileinfo->get_params();
+            if (strlen($params['filearea']) &&
+                    ($params['filepath'] === '/' || empty($params['filepath'])) &&
+                    ($params['filename'] === '.' || empty($params['filename'])) &&
+                    context::instance_by_id($params['contextid'])->contextlevel == CONTEXT_MODULE) {
+                if ($parent === -1) {
+                    $parent = $fileinfo->get_parent();
+                }
+                // This is a filearea inside an activity, it can be skipped if it has no non-empty siblings
+                if ($parent && ($parent instanceof file_info_context_module)) {
+                    if ($parent->count_non_empty_children($extensions, 2) <= 1) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
     }
 
     /**
-     * Returns node for $ret['list']
+     * Converts file_info object to element of repository return list
      *
+     * @param file_info $fileinfo
      * @return array
      */
-    public function get_node() {
+    private function get_node(file_info $fileinfo) {
         global $OUTPUT;
-        $encodedpath = base64_encode(serialize($this->fileinfo->get_params()));
+        $encodedpath = base64_encode(serialize($fileinfo->get_params()));
         $node = array(
-            'title' => $this->fileinfo->get_visible_name(),
-            'datemodified' => $this->fileinfo->get_timemodified(),
-            'datecreated' => $this->fileinfo->get_timecreated()
+            'title' => $fileinfo->get_visible_name(),
+            'datemodified' => $fileinfo->get_timemodified(),
+            'datecreated' => $fileinfo->get_timecreated()
         );
-        if ($this->isdir) {
+        if ($fileinfo->is_directory()) {
             $node['path'] = $encodedpath;
             $node['thumbnail'] = $OUTPUT->pix_url(file_folder_icon(90))->out(false);
             $node['children'] = array();
         } else {
-            $node['size'] = $this->fileinfo->get_filesize();
-            $node['author'] = $this->fileinfo->get_author();
-            $node['license'] = $this->fileinfo->get_license();
-            $node['isref'] = $this->fileinfo->is_external_file();
-            if ($this->fileinfo->get_status() == 666) {
+            $node['size'] = $fileinfo->get_filesize();
+            $node['author'] = $fileinfo->get_author();
+            $node['license'] = $fileinfo->get_license();
+            $node['isref'] = $fileinfo->is_external_file();
+            if ($fileinfo->get_status() == 666) {
                 $node['originalmissing'] = true;
             }
             $node['source'] = $encodedpath;
-            $node['thumbnail'] = $OUTPUT->pix_url(file_file_icon($this->fileinfo, 90))->out(false);
-            $node['icon'] = $OUTPUT->pix_url(file_file_icon($this->fileinfo, 24))->out(false);
-            if ($imageinfo = $this->fileinfo->get_imageinfo()) {
+            $node['thumbnail'] = $OUTPUT->pix_url(file_file_icon($fileinfo, 90))->out(false);
+            $node['icon'] = $OUTPUT->pix_url(file_file_icon($fileinfo, 24))->out(false);
+            if ($imageinfo = $fileinfo->get_imageinfo()) {
                 // what a beautiful picture, isn't it
-                $fileurl = new moodle_url($this->fileinfo->get_url());
-                $node['realthumbnail'] = $fileurl->out(false, array('preview' => 'thumb', 'oid' => $this->fileinfo->get_timemodified()));
-                $node['realicon'] = $fileurl->out(false, array('preview' => 'tinyicon', 'oid' => $this->fileinfo->get_timemodified()));
+                $fileurl = new moodle_url($fileinfo->get_url());
+                $node['realthumbnail'] = $fileurl->out(false, array('preview' => 'thumb', 'oid' => $fileinfo->get_timemodified()));
+                $node['realicon'] = $fileurl->out(false, array('preview' => 'tinyicon', 'oid' => $fileinfo->get_timemodified()));
                 $node['image_width'] = $imageinfo['width'];
                 $node['image_height'] = $imageinfo['height'];
             }
@@ -250,155 +250,16 @@ class repository_local_file {
     }
 
     /**
-     * Returns node for $ret['path']
+     * Converts file_info object to element of repository return path
      *
+     * @param file_info $fileinfo
      * @return array
      */
-    public function get_node_path() {
-        $encodedpath = base64_encode(serialize($this->fileinfo->get_params()));
+    private function get_node_path(file_info $fileinfo) {
+        $encodedpath = base64_encode(serialize($fileinfo->get_params()));
         return array(
             'path' => $encodedpath,
-            'name' => $this->fileinfo->get_visible_name()
+            'name' => $fileinfo->get_visible_name()
         );
     }
-
-    /**
-     * Checks if this is a directory
-     *
-     * @return bool
-     */
-    public function is_dir() {
-        return $this->isdir;
-    }
-
-    /**
-     * Returns children of this element
-     *
-     * @return array
-     */
-    public function get_children() {
-        if (!$this->isdir) {
-            return array();
-        }
-        if ($this->children === null) {
-            $this->children = array();
-            $children = $this->fileinfo->get_children();
-            for ($i=0; $i<count($children); $i++) {
-                $this->children[] = self::retrieve_file_info($children[$i], $this->repository, $this);
-            }
-        }
-        return $this->children;
-    }
-
-    /**
-     * Checks if this folder is empty (contains no non-empty children)
-     *
-     * @return bool
-     */
-    public function is_empty() {
-        if ($this->isempty === null) {
-            $this->isempty = true;
-            if (!$this->fileinfo->is_empty_area()) {
-                // even if is_empty_area() returns false, element still may be empty
-                $children = $this->get_children();
-                if (!empty($children)) {
-                    // 1. Let's look at already retrieved children
-                    foreach ($children as $childnode) {
-                        if ($childnode->isempty === false) {
-                            // we already calculated isempty for a child, and it is not empty
-                            $this->isempty = false;
-                            break;
-                        }
-                    }
-                    if ($this->isempty) {
-                        // 2. now we know that this directory contains children that are either empty or we don't know
-                        foreach ($children as $childnode) {
-                            if (!$childnode->is_empty()) {
-                                $this->isempty = false;
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return $this->isempty;
-    }
-
-    /**
-     * Returns the parent element
-     *
-     * @return repository_local_file
-     */
-    public function get_parent() {
-        if ($this->parent === null) {
-            if ($parent = $this->fileinfo->get_parent()) {
-                $this->parent = self::retrieve_file_info($parent, $this->repository);
-            } else {
-                $this->parent = false;
-            }
-        }
-        return $this->parent;
-    }
-
-    /**
-     * Wether this folder may be skipped in tree view
-     *
-     * @return bool
-     */
-    public function can_skip() {
-        global $CFG;
-        if ($this->skip === null) {
-            $this->skip = false;
-            if ($this->fileinfo instanceof file_info_stored) {
-                $params = $this->fileinfo->get_params();
-                if (strlen($params['filearea']) && $params['filepath'] == '/' && $params['filename'] == '.') {
-                    // This is a filearea inside an activity, it can be skipped if it has no non-empty siblings
-                    if ($parent = $this->get_parent()) {
-                        $siblings = $parent->get_children();
-                        $countnonempty = 0;
-                        foreach ($siblings as $sibling) {
-                            if (!$sibling->is_empty()) {
-                                $countnonempty++;
-                                if ($countnonempty > 1) {
-                                    break;
-                                }
-                            }
-                        }
-                        if ($countnonempty <= 1) {
-                            $this->skip = true;
-                        }
-                    }
-                }
-            } else if ($this->fileinfo instanceof file_info_context_coursecat) {
-                // This is a course category. For non-admins we do not display categories
-                $this->skip = empty($CFG->navshowmycoursecategories) &&
-                        !has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM));
-            }
-        }
-        return $this->skip;
-    }
-
-    /**
-     * Returns array of children who have any elmenets
-     *
-     * If a subfolder can be skipped - list children of subfolder instead
-     * (recursive function)
-     *
-     * @return array
-     */
-    public function get_non_empty_children() {
-        $children = $this->get_children();
-        $nonemptychildren = array();
-        foreach ($children as $child) {
-            if (!$child->is_empty()) {
-                if ($child->can_skip()) {
-                    $nonemptychildren = array_merge($nonemptychildren, $child->get_non_empty_children());
-                } else {
-                    $nonemptychildren[] = $child;
-                }
-            }
-        }
-        return $nonemptychildren;
-    }
 }
index a8b573d..c2d54c8 100644 (file)
@@ -1,19 +1,24 @@
 <?php
 
-$hasheading = ($PAGE->heading);
-$hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar());
-$hasfooter = (empty($PAGE->layout_options['nofooter']));
 $hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
 $hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
+$showsidepre = $hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT);
+$showsidepost = $hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT);
+
+$custommenu = $OUTPUT->custom_menu();
+$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
 
 $bodyclasses = array();
-if ($hassidepre && !$hassidepost) {
+if ($showsidepre && !$showsidepost) {
     $bodyclasses[] = 'side-pre-only';
-} else if ($hassidepost && !$hassidepre) {
+} else if ($showsidepost && !$showsidepre) {
     $bodyclasses[] = 'side-post-only';
-} else if (!$hassidepost && !$hassidepre) {
+} else if (!$showsidepost && !$showsidepre) {
     $bodyclasses[] = 'content-only';
 }
+if ($hascustommenu) {
+    $bodyclasses[] = 'has_custom_menu';
+}
 
 echo $OUTPUT->doctype() ?>
 <html <?php echo $OUTPUT->htmlattributes() ?>>
@@ -30,20 +35,24 @@ echo $OUTPUT->doctype() ?>
 
 <!-- START OF HEADER -->
 
-       <div id="wrapper" class="clearfix">
-
-       <div id="page-header">
-                       <div id="page-header-wrapper" class="clearfix">
-                       <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
-                   <div class="headermenu">
-                       <?php
-                           echo $OUTPUT->login_info();
-                       echo $OUTPUT->lang_menu();
-                                   echo $PAGE->headingmenu;
-                               ?>
-                       </div>
-               </div>
-           </div>
+    <div id="wrapper" class="clearfix">
+
+        <div id="page-header">
+            <div id="page-header-wrapper" class="clearfix">
+                <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
+                <div class="headermenu">
+                    <?php
+                        echo $OUTPUT->login_info();
+                        echo $OUTPUT->lang_menu();
+                        echo $PAGE->headingmenu;
+                    ?>
+                </div>
+            </div>
+        </div>
+
+        <?php if ($hascustommenu) { ?>
+            <div id="custommenu"><?php echo $custommenu; ?></div>
+        <?php } ?>
 
         <div class="clearer"></div> <!-- temporarily added on 06/25/10 -->
 
@@ -51,60 +60,60 @@ echo $OUTPUT->doctype() ?>
 
 <!-- START OF CONTENT -->
 
-               <div id="page-content-wrapper" class="clearfix">
-               <div id="page-content">
-                   <div id="region-main-box">
-                   <div id="region-post-box">
-
-                           <div id="region-main-wrap">
-                           <div id="region-main">
-                                   <div class="region-content">
-                                   <?php echo $OUTPUT->main_content() ?>
-                                       </div>
-                                   </div>
-                           </div>
-
-                               <?php if ($hassidepre) { ?>
-                           <div id="region-pre">
-                                   <div class="region-content">
-                                   <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
-                                   </div>
-                           </div>
-                               <?php } ?>
-
-                               <?php if ($hassidepost) { ?>
-                           <div id="region-post">
-                                   <div class="region-content">
-                                   <?php echo $OUTPUT->blocks_for_region('side-post') ?>
-                                   </div>
-                               </div>
-                           <?php } ?>
-
-                           </div>
-                   </div>
-                   </div>
-               </div>
+        <div id="page-content-wrapper" class="clearfix">
+            <div id="page-content">
+                <div id="region-main-box">
+                    <div id="region-post-box">
+
+                        <div id="region-main-wrap">
+                            <div id="region-main">
+                                <div class="region-content">
+                                    <?php echo $OUTPUT->main_content() ?>
+                                </div>
+                            </div>
+                        </div>
+
+                        <?php if ($hassidepre) { ?>
+                        <div id="region-pre" class="block-region">
+                            <div class="region-content">
+                                <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
+                            </div>
+                        </div>
+                        <?php } ?>
+
+                        <?php if ($hassidepost) { ?>
+                        <div id="region-post" class="block-region">
+                            <div class="region-content">
+                                <?php echo $OUTPUT->blocks_for_region('side-post') ?>
+                            </div>
+                        </div>
+                        <?php } ?>
+
+                    </div>
+                </div>
+            </div>
+        </div>
 
 <!-- END OF CONTENT -->
 
 <!-- START OF FOOTER -->
 
-           <div id="page-footer">
-           <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
+        <div id="page-footer">
+            <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
 
-               <?php
-                       echo $OUTPUT->login_info();
-                   echo $OUTPUT->home_link();
-                       echo $OUTPUT->standard_footer_html();
-               ?>
-       </div>
+            <?php
+                echo $OUTPUT->login_info();
+                echo $OUTPUT->home_link();
+                echo $OUTPUT->standard_footer_html();
+            ?>
+        </div>
 
 <!-- END OF FOOTER -->
 
-       </div> <!-- END #wrapper -->
+    </div> <!-- END #wrapper -->
 
 </div><!-- END #page -->
 
 <?php echo $OUTPUT->standard_end_of_body_html() ?>
 </body>
-</html>
\ No newline at end of file
+</html>
index cda6005..5261314 100644 (file)
@@ -3,17 +3,27 @@
 $hasheading = ($PAGE->heading);
 $hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar());
 $hasfooter = (empty($PAGE->layout_options['nofooter']));
-$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
-$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
+$hassidepre = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-pre', $OUTPUT));
+$hassidepost = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-post', $OUTPUT));
+$haslogininfo = (empty($PAGE->layout_options['nologininfo']));
+
+$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT));
+$showsidepost = ($hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT));
+
+$custommenu = $OUTPUT->custom_menu();
+$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
 
 $bodyclasses = array();
-if ($hassidepre && !$hassidepost) {
+if ($showsidepre && !$showsidepost) {
     $bodyclasses[] = 'side-pre-only';
-} else if ($hassidepost && !$hassidepre) {
+} else if ($showsidepost && !$showsidepre) {
     $bodyclasses[] = 'side-post-only';
-} else if (!$hassidepost && !$hassidepre) {
+} else if (!$showsidepost && !$showsidepre) {
     $bodyclasses[] = 'content-only';
 }
+if ($hascustommenu) {
+    $bodyclasses[] = 'has_custom_menu';
+}
 
 echo $OUTPUT->doctype() ?>
 <html <?php echo $OUTPUT->htmlattributes() ?>>
@@ -49,6 +59,10 @@ echo $OUTPUT->doctype() ?>
             </div>
         </div>
 
+        <?php if ($hascustommenu) { ?>
+            <div id="custommenu"><?php echo $custommenu; ?></div>
+        <?php } ?>
+
         <?php if ($hasnavbar) { ?>
             <div class="navbar clearfix">
                 <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
@@ -76,7 +90,7 @@ echo $OUTPUT->doctype() ?>
                         </div>
 
                         <?php if ($hassidepre) { ?>
-                        <div id="region-pre">
+                        <div id="region-pre" class="block-region">
                             <div class="region-content">
                                 <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
                             </div>
@@ -84,7 +98,7 @@ echo $OUTPUT->doctype() ?>
                         <?php } ?>
 
                         <?php if ($hassidepost) { ?>
-                        <div id="region-post">
+                        <div id="region-post" class="block-region">
                             <div class="region-content">
                                 <?php echo $OUTPUT->blocks_for_region('side-post') ?>
                             </div>
@@ -119,4 +133,4 @@ echo $OUTPUT->doctype() ?>
 
 <?php echo $OUTPUT->standard_end_of_body_html() ?>
 </body>
-</html>
\ No newline at end of file
+</html>
index fce6e89..06607e3 100644 (file)
@@ -3,17 +3,23 @@
 $hasheading = ($PAGE->heading);
 $hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar());
 $hasfooter = (empty($PAGE->layout_options['nofooter']));
-$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
-$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
+$hassidepre = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-pre', $OUTPUT));
+$haslogininfo = (empty($PAGE->layout_options['nologininfo']));
+
+$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT));
+
+$custommenu = $OUTPUT->custom_menu();
+$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
 
 $bodyclasses = array();
-if ($hassidepre && !$hassidepost) {
+if ($showsidepre ) {
     $bodyclasses[] = 'side-pre-only';
-} else if ($hassidepost && !$hassidepre) {
-    $bodyclasses[] = 'side-post-only';
-} else if (!$hassidepost && !$hassidepre) {
+} else {
     $bodyclasses[] = 'content-only';
 }
+if ($hascustommenu) {
+    $bodyclasses[] = 'has_custom_menu';
+}
 
 echo $OUTPUT->doctype() ?>
 <html <?php echo $OUTPUT->htmlattributes() ?>>
@@ -49,6 +55,10 @@ echo $OUTPUT->doctype() ?>
             </div>
         </div>
 
+        <?php if ($hascustommenu) { ?>
+            <div id="custommenu"><?php echo $custommenu; ?></div>
+        <?php } ?>
+
         <?php if ($hasnavbar) { ?>
             <div class="navbar clearfix">
                 <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
@@ -106,4 +116,4 @@ echo $OUTPUT->doctype() ?>
 
 <?php echo $OUTPUT->standard_end_of_body_html() ?>
 </body>
-</html>
\ No newline at end of file
+</html>
index 4fc7f14..223594a 100644 (file)
@@ -30,7 +30,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012062502.06;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012062502.08;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
index 5a14d57..326091a 100644 (file)
@@ -564,7 +564,7 @@ class webservice {
             //detect the missing capabilities
             foreach ($servicecaps as $functioname => $functioncaps) {
                 foreach ($functioncaps as $functioncap) {
-                    if (!key_exists($functioncap, $usercaps)) {
+                    if (!array_key_exists($functioncap, $usercaps)) {
                         if (!isset($usersmissingcaps[$user->id])
                                 or array_search($functioncap, $usersmissingcaps[$user->id]) === false) {
                             $usersmissingcaps[$user->id][] = $functioncap;