Merge branch 'MDL-44929-master' of git://github.com/andrewnicols/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 17 Jun 2014 09:04:11 +0000 (11:04 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 17 Jun 2014 09:04:11 +0000 (11:04 +0200)
162 files changed:
admin/renderer.php
admin/settings/appearance.php
admin/tool/log/lang/en/tool_log.php
admin/tool/uploadcourse/classes/course.php
auth/db/auth.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/backup_structure_dbops.class.php
backup/util/structure/backup_structure_processor.class.php
backup/util/ui/tests/behat/backup_courses.feature
backup/util/ui/tests/behat/behat_backup.php
backup/util/ui/tests/behat/restore_moodle2_courses.feature
blocks/navigation/tests/behat/expand_courses_node.feature [new file with mode: 0644]
blocks/navigation/tests/behat/expand_my_courses_setting.feature [new file with mode: 0644]
blocks/navigation/tests/behat/view_my_courses.feature
composer.json
course/reset_form.php
enrol/self/tests/behat/self_enrolment.feature
grade/grading/form/guide/lib.php
grade/grading/form/rubric/lib.php
grade/grading/tests/behat/behat_grading.php
group/group_form.php
group/tests/behat/create_groups.feature
group/tests/behat/update_groups.feature
lang/en/admin.php
lang/en/cache.php
lang/en/error.php
lang/en/group.php
lang/en/moodle.php
lib/behat/form_field/behat_form_editor.php
lib/behat/form_field/behat_form_field.php
lib/classes/event/message_sent.php
lib/classes/grading_external.php
lib/classes/message/manager.php [new file with mode: 0644]
lib/classes/task/scheduled_task.php
lib/classes/useragent.php
lib/db/caches.php
lib/db/services.php
lib/db/upgrade.php
lib/db/upgradelib.php
lib/dml/moodle_database.php
lib/editor/atto/plugins/accessibilitychecker/tests/behat/accessibilitychecker.feature [new file with mode: 0644]
lib/editor/atto/plugins/accessibilityhelper/tests/behat/accessibilityhelper.feature [new file with mode: 0644]
lib/editor/atto/plugins/align/tests/behat/align.feature [new file with mode: 0644]
lib/editor/atto/plugins/bold/tests/behat/bold.feature [new file with mode: 0644]
lib/editor/atto/plugins/charmap/tests/behat/charmap.feature [new file with mode: 0644]
lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button-debug.js
lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button-min.js
lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button.js
lib/editor/atto/plugins/charmap/yui/src/button/js/button.js
lib/editor/atto/plugins/clear/tests/behat/clear.feature [new file with mode: 0644]
lib/editor/atto/plugins/collapse/tests/behat/collapse.feature [new file with mode: 0644]
lib/editor/atto/plugins/equation/tests/behat/equation.feature [new file with mode: 0644]
lib/editor/atto/plugins/html/tests/behat/html.feature [new file with mode: 0644]
lib/editor/atto/plugins/image/tests/behat/image.feature [new file with mode: 0644]
lib/editor/atto/plugins/indent/tests/behat/indent.feature [new file with mode: 0644]
lib/editor/atto/plugins/italic/tests/behat/italic.feature [new file with mode: 0644]
lib/editor/atto/plugins/link/tests/behat/link.feature [new file with mode: 0644]
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
lib/editor/atto/plugins/link/yui/src/button/js/button.js
lib/editor/atto/plugins/media/tests/behat/media.feature [new file with mode: 0644]
lib/editor/atto/plugins/orderedlist/tests/behat/orderedlist.feature [new file with mode: 0644]
lib/editor/atto/plugins/strike/tests/behat/strike.feature [new file with mode: 0644]
lib/editor/atto/plugins/subscript/tests/behat/subscript.feature [new file with mode: 0644]
lib/editor/atto/plugins/superscript/tests/behat/superscript.feature [new file with mode: 0644]
lib/editor/atto/plugins/table/tests/behat/table.feature [new file with mode: 0644]
lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js
lib/editor/atto/plugins/table/yui/src/button/js/button.js
lib/editor/atto/plugins/title/tests/behat/title.feature [new file with mode: 0644]
lib/editor/atto/plugins/underline/tests/behat/underline.feature [new file with mode: 0644]
lib/editor/atto/plugins/unorderedlist/tests/behat/unorderedlist.feature [new file with mode: 0644]
lib/editor/atto/tests/behat/behat_editor_atto.php [new file with mode: 0644]
lib/editor/atto/tests/fixtures/moodle-logo.png [new file with mode: 0644]
lib/editor/atto/tests/fixtures/moodle-logo.webm [new file with mode: 0644]
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js
lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js
lib/editor/tinymce/tests/behat/edit_available_icons.feature
lib/excellib.class.php
lib/filelib.php
lib/filestorage/stored_file.php
lib/filestorage/tests/file_storage_test.php
lib/messagelib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputcomponents.php
lib/phpexcel/PHPExcel/Shared/Excel5.php [deleted file]
lib/phpexcel/PHPExcel/Shared/OLE.php [deleted file]
lib/phpexcel/PHPExcel/Shared/OLE/ChainedBlockStream.php [deleted file]
lib/phpexcel/PHPExcel/Shared/OLE/PPS.php [deleted file]
lib/phpexcel/PHPExcel/Shared/OLE/PPS/File.php [deleted file]
lib/phpexcel/PHPExcel/Shared/OLE/PPS/Root.php [deleted file]
lib/phpexcel/PHPExcel/Shared/OLERead.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/BIFFwriter.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/Escher.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/Font.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/Parser.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/Workbook.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/Worksheet.php [deleted file]
lib/phpexcel/PHPExcel/Writer/Excel5/Xf.php [deleted file]
lib/phpexcel/readme_moodle.txt
lib/questionlib.php
lib/tests/grading_externallib_test.php
lib/tests/messagelib_test.php
lib/tests/other/spreadsheettestpage.php
lib/tests/scheduled_task_test.php
lib/tests/theme_config_test.php
lib/upgrade.txt
mod/assign/batchsetallocatedmarkerform.php
mod/assign/batchsetmarkingworkflowstateform.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/editpdf/classes/page_editor.php
mod/assign/feedback/editpdf/locallib.php
mod/assign/locallib.php
mod/assign/tests/base_test.php
mod/assign/tests/behat/outcome_grading.feature [new file with mode: 0644]
mod/assign/tests/events_test.php
mod/data/locallib.php
mod/feedback/lib.php
mod/lti/return.php
mod/lti/settings.php
mod/quiz/editlib.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/startattempt.php
mod/quiz/styles.css
mod/quiz/tests/editlib_test.php
mod/quiz/tests/events_test.php
mod/quiz/tests/lib_test.php
mod/quiz/upgrade.txt
mod/scorm/locallib.php
mod/scorm/module.js
mod/wiki/classes/event/comments_viewed.php
mod/workshop/classes/event/submission_reassessed.php
pix/f/publisher-128.png [new file with mode: 0644]
pix/f/publisher-24.png [new file with mode: 0644]
pix/f/publisher-256.png [new file with mode: 0644]
pix/f/publisher-32.png [new file with mode: 0644]
pix/f/publisher-48.png [new file with mode: 0644]
pix/f/publisher-64.png [new file with mode: 0644]
pix/f/publisher-72.png [new file with mode: 0644]
pix/f/publisher-80.png [new file with mode: 0644]
pix/f/publisher-96.png [new file with mode: 0644]
pix/f/publisher.png [new file with mode: 0644]
question/type/questiontypebase.php
question/type/random/backup/moodle2/restore_qtype_random_plugin.class.php
question/type/random/db/upgrade.php [new file with mode: 0644]
question/type/random/questiontype.php
question/type/random/version.php
report/log/index.php
report/participation/index.php
theme/base/style/core.css
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/style/moodle.css
version.php

index 944b501..954b977 100644 (file)
@@ -1384,7 +1384,7 @@ class core_admin_renderer extends plugin_renderer_base {
             get_string('report'),
             get_string('status'),
         );
-        $servertable->colclasses = array('centeralign name', 'centeralign status', 'leftalign report', 'centeralign info');
+        $servertable->colclasses = array('centeralign name', 'centeralign info', 'leftalign report', 'centeralign status');
         $servertable->attributes['class'] = 'admintable environmenttable generaltable';
         $servertable->id = 'serverstatus';
 
index 1df0a09..fd31903 100644 (file)
@@ -122,6 +122,7 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // sp
     $temp->add(new admin_setting_configcheckbox('navshowcategories', new lang_string('navshowcategories', 'admin'), new lang_string('confignavshowcategories', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('navshowmycoursecategories', new lang_string('navshowmycoursecategories', 'admin'), new lang_string('navshowmycoursecategories_help', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('navshowallcourses', new lang_string('navshowallcourses', 'admin'), new lang_string('confignavshowallcourses', 'admin'), 0));
+    $temp->add(new admin_setting_configcheckbox('navexpandmycourses', new lang_string('navexpandmycourses', 'admin'), new lang_string('navexpandmycourses_desc', 'admin'), 1));
     $sortoptions = array(
         'sortorder' => new lang_string('sort_sortorder', 'admin'),
         'fullname' => new lang_string('sort_fullname', 'admin'),
index 62f4a48..c57c424 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 $string['actlogshdr'] = 'Available log stores';
-$string['configlogplugins'] = 'Please enable all required plugins and arrange then in appropriate order.';
+$string['configlogplugins'] = 'Please enable all required plugins and arrange them in appropriate order.';
 $string['logging'] = 'Logging';
 $string['managelogging'] = 'Manage log stores';
 $string['reportssupported'] = 'Reports supported';
index 4691ead..ff22833 100644 (file)
@@ -875,7 +875,6 @@ class tool_uploadcourse_course {
         $resetdata = new stdClass();
         $resetdata->id = $course->id;
         $resetdata->reset_start_date = time();
-        $resetdata->reset_logs = true;
         $resetdata->reset_events = true;
         $resetdata->reset_notes = true;
         $resetdata->delete_blog_associations = true;
index 3b80e26..faccb6d 100644 (file)
@@ -260,6 +260,8 @@ class auth_plugin_db extends auth_plugin_base {
     function sync_users(progress_trace $trace, $do_updates=false) {
         global $CFG, $DB;
 
+        require_once($CFG->dirroot . '/user/lib.php');
+
         // List external users.
         $userlist = $this->get_userlist();
 
@@ -289,7 +291,6 @@ class auth_plugin_db extends auth_plugin_base {
             $remove_users = $DB->get_records_sql($sql, $params);
 
             if (!empty($remove_users)) {
-                require_once($CFG->dirroot.'/user/lib.php');
                 $trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
 
                 foreach ($remove_users as $user) {
@@ -381,9 +382,14 @@ class auth_plugin_db extends auth_plugin_base {
             foreach($add_users as $user) {
                 $username = $user;
                 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
-                    if ($old_user = $DB->get_record('user', array('username'=>$username, 'deleted'=>0, 'suspended'=>1, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
-                        $DB->set_field('user', 'suspended', 0, array('id'=>$old_user->id));
-                        $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name'=>$username, 'id'=>$old_user->id)), 1);
+                    if ($olduser = $DB->get_record('user', array('username' => $username, 'deleted' => 0, 'suspended' => 1,
+                            'mnethostid' => $CFG->mnet_localhost_id, 'auth' => $this->authtype))) {
+                        $updateuser = new stdClass();
+                        $updateuser->id = $olduser->id;
+                        $updateuser->suspended = 0;
+                        user_update_user($updateuser);
+                        $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name' => $username,
+                            'id' => $olduser->id)), 1);
                         continue;
                     }
                 }
@@ -399,17 +405,12 @@ class auth_plugin_db extends auth_plugin_base {
                 if (empty($user->lang)) {
                     $user->lang = $CFG->lang;
                 }
-                if (empty($user->calendartype)) {
-                    $user->calendartype = $CFG->calendartype;
-                }
-                $user->timecreated = time();
-                $user->timemodified = $user->timecreated;
                 if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) {
                     $trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)), 1);
                     continue;
                 }
                 try {
-                    $id = $DB->insert_record ('user', $user); // it is truly a new user
+                    $id = user_create_user($user, false); // It is truly a new user.
                     $trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)), 1);
                 } catch (moodle_exception $e) {
                     $trace->output(get_string('auth_dbinsertusererror', 'auth_db', $user->username), 1);
@@ -521,8 +522,10 @@ class auth_plugin_db extends auth_plugin_base {
 
         // Ensure userid is not overwritten.
         $userid = $user->id;
-        $updated = false;
+        $needsupdate = false;
 
+        $updateuser = new stdClass();
+        $updateuser->id = $userid;
         if ($newinfo = $this->get_userinfo($username)) {
             $newinfo = truncate_userinfo($newinfo);
 
@@ -539,14 +542,15 @@ class auth_plugin_db extends auth_plugin_base {
 
                 if (!empty($this->config->{'field_updatelocal_' . $key})) {
                     if (isset($user->{$key}) and $user->{$key} != $value) { // Only update if it's changed.
-                        $DB->set_field('user', $key, $value, array('id'=>$userid));
-                        $updated = true;
+                        $needsupdate = true;
+                        $updateuser->$key = $value;
                     }
                 }
             }
         }
-        if ($updated) {
-            $DB->set_field('user', 'timemodified', time(), array('id'=>$userid));
+        if ($needsupdate) {
+            require_once($CFG->dirroot . '/user/lib.php');
+            user_update_user($updateuser);
         }
         return $DB->get_record('user', array('id'=>$userid, 'deleted'=>0));
     }
index 53cce22..f767133 100644 (file)
@@ -28,8 +28,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 /**
- * create the temp dir where backup/restore will happen,
- * delete old directories and create temp ids table
+ * Create the temp dir where backup/restore will happen and create temp ids table.
  */
 class create_and_clean_temp_stuff extends backup_execution_step {
 
@@ -38,7 +37,6 @@ class create_and_clean_temp_stuff extends backup_execution_step {
         $progress->start_progress('Deleting backup directories');
         backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
         backup_helper::clear_backup_dir($this->get_backupid(), $progress);           // Empty temp dir, just in case
-        backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60), $progress);    // Delete > 4 hours temp dirs
         backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
         backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
         $progress->end_progress();
@@ -46,11 +44,11 @@ class create_and_clean_temp_stuff extends backup_execution_step {
 }
 
 /**
- * delete the temp dir used by backup/restore (conditionally),
- * delete old directories and drop tem ids table. Note we delete
+ * Delete the temp dir used by backup/restore (conditionally),
+ * delete old directories and drop temp ids table. Note we delete
  * the directory but not the corresponding log file that will be
- * there for, at least, 4 hours - only delete_old_backup_dirs()
- * deletes log files (for easier access to them)
+ * there for, at least, 1 week - only delete_old_backup_dirs() or cron
+ * deletes log files (for easier access to them).
  */
 class drop_and_clean_temp_stuff extends backup_execution_step {
 
@@ -60,7 +58,7 @@ class drop_and_clean_temp_stuff extends backup_execution_step {
         global $CFG;
 
         backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
-        backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60));              // Delete > 4 hours temp dirs
+        backup_helper::delete_old_backup_dirs(strtotime('-1 week'));                // Delete > 1 week old temp dirs.
         // Delete temp dir conditionally:
         // 1) If $CFG->keeptempdirectoriesonbackup is not enabled
         // 2) If backup temp dir deletion has been marked to be avoided
index 512f64d..6e7a461 100644 (file)
@@ -67,7 +67,7 @@ class restore_drop_and_clean_temp_stuff extends restore_execution_step {
         restore_controller_dbops::drop_restore_temp_tables($this->get_restoreid()); // Drop ids temp table
         $progress = $this->task->get_progress();
         $progress->start_progress('Deleting backup dir');
-        backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60), $progress);              // Delete > 4 hours temp dirs
+        backup_helper::delete_old_backup_dirs(strtotime('-1 week'), $progress);      // Delete > 1 week old temp dirs.
         if (empty($CFG->keeptempdirectoriesonbackup)) { // Conditionally
             backup_helper::delete_backup_dir($this->task->get_tempdir(), $progress); // Empty restore dir
         }
@@ -3676,13 +3676,16 @@ class restore_move_module_questions_categories extends restore_execution_step {
  * Execution step that will create all the question/answers/qtype-specific files for the restored
  * questions. It must be executed after {@link restore_move_module_questions_categories}
  * because only then each question is in its final category and only then the
- * context can be determined
- *
- * TODO: Improve this. Instead of looping over each question, it can be reduced to
- *       be done by contexts (this will save a huge ammount of queries)
+ * contexts can be determined.
  */
 class restore_create_question_files extends restore_execution_step {
 
+    /** @var array Question-type specific component items cache. */
+    private $qtypecomponentscache = array();
+
+    /**
+     * Preform the restore_create_question_files step.
+     */
     protected function define_execution() {
         global $DB;
 
@@ -3690,60 +3693,93 @@ class restore_create_question_files extends restore_execution_step {
         $progress = $this->task->get_progress();
         $progress->start_progress($this->get_name(), \core\progress\base::INDETERMINATE);
 
-        // Let's process only created questions
-        $questionsrs = $DB->get_recordset_sql("SELECT bi.itemid, bi.newitemid, bi.parentitemid, q.qtype
+        // Parentitemids of question_createds in backup_ids_temp are the category it is in.
+        // MUST use a recordset, as there is no unique key in the first (or any) column.
+        $catqtypes = $DB->get_recordset_sql("SELECT DISTINCT bi.parentitemid AS categoryid, q.qtype as qtype
                                                FROM {backup_ids_temp} bi
                                                JOIN {question} q ON q.id = bi.newitemid
                                               WHERE bi.backupid = ?
-                                                AND bi.itemname = 'question_created'", array($this->get_restoreid()));
-        foreach ($questionsrs as $question) {
-            // Report progress for each question.
-            $progress->progress();
-
-            // Get question_category mapping, it contains the target context for the question
-            if (!$qcatmapping = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'question_category', $question->parentitemid)) {
-                // Something went really wrong, cannot find the question_category for the question
-                debugging('Error fetching target context for question', DEBUG_DEVELOPER);
-                continue;
-            }
-            // Calculate source and target contexts
-            $oldctxid = $qcatmapping->info->contextid;
-            $newctxid = $qcatmapping->parentitemid;
-
-            // Add common question files (question and question_answer ones)
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'questiontext',
-                    $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'generalfeedback',
-                    $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answer',
-                    $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answerfeedback',
-                    $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'hint',
-                    $oldctxid, $this->task->get_userid(), 'question_hint', null, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'correctfeedback',
-                    $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'partiallycorrectfeedback',
-                    $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true, $progress);
-            restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'incorrectfeedback',
-                    $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true, $progress);
-
-            // Add qtype dependent files
-            $components = backup_qtype_plugin::get_components_and_fileareas($question->qtype);
-            foreach ($components as $component => $fileareas) {
-                foreach ($fileareas as $filearea => $mapping) {
-                    // Use itemid only if mapping is question_created
-                    $itemid = ($mapping == 'question_created') ? $question->itemid : null;
-                    restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component, $filearea,
-                            $oldctxid, $this->task->get_userid(), $mapping, $itemid, $newctxid, true, $progress);
+                                                AND bi.itemname = 'question_created'
+                                           ORDER BY categoryid ASC", array($this->get_restoreid()));
+
+        $currentcatid = -1;
+        foreach ($catqtypes as $categoryid => $row) {
+            $qtype = $row->qtype;
+
+            // Check if we are in a new category.
+            if ($currentcatid !== $categoryid) {
+                // Report progress for each category.
+                $progress->progress();
+
+                if (!$qcatmapping = restore_dbops::get_backup_ids_record($this->get_restoreid(),
+                        'question_category', $categoryid)) {
+                    // Something went really wrong, cannot find the question_category for the question_created records.
+                    debugging('Error fetching target context for question', DEBUG_DEVELOPER);
+                    continue;
                 }
+
+                // Calculate source and target contexts.
+                $oldctxid = $qcatmapping->info->contextid;
+                $newctxid = $qcatmapping->parentitemid;
+
+                $this->send_common_files($oldctxid, $newctxid, $progress);
+                $currentcatid = $categoryid;
             }
+
+            $this->send_qtype_files($qtype, $oldctxid, $newctxid, $progress);
         }
-        $questionsrs->close();
+        $catqtypes->close();
         $progress->end_progress();
     }
-}
 
+    /**
+     * Send the common question files to a new context.
+     *
+     * @param int             $oldctxid Old context id.
+     * @param int             $newctxid New context id.
+     * @param \core\progress  $progress Progress object to use.
+     */
+    private function send_common_files($oldctxid, $newctxid, $progress) {
+        // Add common question files (question and question_answer ones).
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'questiontext',
+                $oldctxid, $this->task->get_userid(), 'question_created', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'generalfeedback',
+                $oldctxid, $this->task->get_userid(), 'question_created', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answer',
+                $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answerfeedback',
+                $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'hint',
+                $oldctxid, $this->task->get_userid(), 'question_hint', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'correctfeedback',
+                $oldctxid, $this->task->get_userid(), 'question_created', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'partiallycorrectfeedback',
+                $oldctxid, $this->task->get_userid(), 'question_created', null, $newctxid, true, $progress);
+        restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'incorrectfeedback',
+                $oldctxid, $this->task->get_userid(), 'question_created', null, $newctxid, true, $progress);
+    }
+
+    /**
+     * Send the question type specific files to a new context.
+     *
+     * @param text            $qtype The qtype name to send.
+     * @param int             $oldctxid Old context id.
+     * @param int             $newctxid New context id.
+     * @param \core\progress  $progress Progress object to use.
+     */
+    private function send_qtype_files($qtype, $oldctxid, $newctxid, $progress) {
+        if (!isset($this->qtypecomponentscache[$qtype])) {
+            $this->qtypecomponentscache[$qtype] = backup_qtype_plugin::get_components_and_fileareas($qtype);
+        }
+        $components = $this->qtypecomponentscache[$qtype];
+        foreach ($components as $component => $fileareas) {
+            foreach ($fileareas as $filearea => $mapping) {
+                restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component, $filearea,
+                        $oldctxid, $this->task->get_userid(), $mapping, null, $newctxid, true, $progress);
+            }
+        }
+    }
+}
 
 /**
  * Try to restore aliases and references to external files.
index 6202906..4ceb221 100644 (file)
@@ -103,7 +103,18 @@ abstract class backup_structure_dbops extends backup_dbops {
         }
     }
 
-    public static function annotate_files($backupid, $contextid, $component, $filearea, $itemid) {
+    /**
+     * Adds backup id database record for all files in the given file area.
+     *
+     * @param string $backupid Backup ID
+     * @param int $contextid Context id
+     * @param string $component Component
+     * @param string $filearea File area
+     * @param int $itemid Item id
+     * @param \core\progress\base $progress
+     */
+    public static function annotate_files($backupid, $contextid, $component, $filearea, $itemid,
+            \core\progress\base $progress = null) {
         global $DB;
         $sql = 'SELECT id
                   FROM {files}
@@ -120,10 +131,19 @@ abstract class backup_structure_dbops extends backup_dbops {
             $sql .= ' AND itemid = ?';
             $params[] = $itemid;
         }
+        if ($progress) {
+            $progress->start_progress('');
+        }
         $rs = $DB->get_recordset_sql($sql, $params);
         foreach ($rs as $record) {
+            if ($progress) {
+                $progress->progress();
+            }
             self::insert_backup_ids_record($backupid, 'file', $record->id);
         }
+        if ($progress) {
+            $progress->end_progress();
+        }
         $rs->close();
     }
 
index 2cc41dc..9085af0 100644 (file)
@@ -86,7 +86,7 @@ class backup_structure_processor extends base_processor {
                 foreach ($area as $filearea => $info) {
                     $contextid = !is_null($info->contextid) ? $info->contextid : $this->get_var(backup::VAR_CONTEXTID);
                     $itemid    = !is_null($info->element) ? $info->element->get_value() : null;
-                    backup_structure_dbops::annotate_files($backupid, $contextid, $component, $filearea, $itemid);
+                    backup_structure_dbops::annotate_files($backupid, $contextid, $component, $filearea, $itemid, $this->progress);
                 }
             }
         }
index f8b2a7a..fdf1c13 100644 (file)
@@ -18,7 +18,7 @@ Feature: Backup Moodle courses
   @javascript
   Scenario: Backup a course providing options
     When I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     Then I should see "Restore"
     And I click on "Restore" "link" in the "test_backup.mbz" "table_row"
     And I should see "URL of backup"
@@ -27,11 +27,11 @@ Feature: Backup Moodle courses
   @javascript
   Scenario: Backup a course with default options
     When I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
-      | Include calendar events | 0 |
-      | Include course logs | 1 |
-      | setting_section_section_5_userinfo | 0 |
-      | setting_section_section_5_included | 0 |
+      | Initial | Include calendar events | 0 |
+      | Initial | Include course logs | 1 |
+      | Schema | setting_section_section_5_userinfo | 0 |
+      | Schema | setting_section_section_5_included | 0 |
+      | Confirmation | Filename | test_backup.mbz |
     Then I should see "Restore"
     And I click on "Restore" "link" in the "test_backup.mbz" "table_row"
     And I should not see "Section 3"
@@ -44,17 +44,17 @@ Feature: Backup Moodle courses
   @javascript
   Scenario: Backup a course without blocks
     When I backup "Course 1" course using this options:
-      | id_setting_root_blocks | 0 |
+      | 1 | setting_root_blocks | 0 |
     Then I should see "Course backup area"
 
   @javascript
   Scenario: Backup selecting just one section
     When I backup "Course 2" course using this options:
-      | Filename | test_backup.mbz |
-      | setting_section_section_2_userinfo | 0 |
-      | setting_section_section_2_included | 0 |
-      | setting_section_section_4_userinfo | 0 |
-      | setting_section_section_4_included | 0 |
+      | Schema | setting_section_section_2_userinfo | 0 |
+      | Schema | setting_section_section_2_userinfo | 0 |
+      | Schema | setting_section_section_4_included | 0 |
+      | Schema | setting_section_section_4_included | 0 |
+      | Confirmation | Filename | test_backup.mbz |
     Then I should see "Course backup area"
     And I click on "Restore" "link" in the "test_backup.mbz" "table_row"
     And I should not see "Section 2"
index 21f82f8..282a470 100644 (file)
@@ -50,34 +50,31 @@ class behat_backup extends behat_base {
      * @param TableNode $options Backup options or false if no options provided
      */
     public function i_backup_course_using_this_options($backupcourse, $options = false) {
-
         // We can not use other steps here as we don't know where the provided data
         // table elements are used, and we need to catch exceptions contantly.
 
         // Go to homepage.
         $this->getSession()->visit($this->locate_path('/'));
-        $this->wait();
 
         // Click the course link.
         $this->find_link($backupcourse)->click();
-        $this->wait();
 
         // Click the backup link.
         $this->find_link(get_string('backup'))->click();
         $this->wait();
 
         // Initial settings.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
         $this->find_button(get_string('backupstage1action', 'backup'))->press();
         $this->wait();
 
         // Schema settings.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
         $this->find_button(get_string('backupstage2action', 'backup'))->press();
         $this->wait();
 
         // Confirmation and review, backup filename can also be specified.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Confirmation"));
         $this->find_button(get_string('backupstage4action', 'backup'))->press();
 
         // Waiting for it to finish.
@@ -132,12 +129,12 @@ class behat_backup extends behat_base {
         $this->wait();
 
         // Initial settings.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
         $this->find_button(get_string('importbackupstage1action', 'backup'))->press();
         $this->wait();
 
         // Schema settings.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
         $this->find_button(get_string('importbackupstage2action', 'backup'))->press();
         $this->wait();
 
@@ -303,12 +300,12 @@ class behat_backup extends behat_base {
         // table elements are used, and we need to catch exceptions contantly.
 
         // Settings.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Settings"));
         $this->find_button(get_string('restorestage4action', 'backup'))->press();
         $this->wait();
 
         // Schema.
-        $this->fill_backup_restore_form($options);
+        $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
         $this->find_button(get_string('restorestage8action', 'backup'))->press();
         $this->wait();
 
@@ -341,23 +338,46 @@ class behat_backup extends behat_base {
             return;
         }
 
-        // Wait for the page to be loaded and the JS ready.
-        $this->wait();
-
         // If we find any of the provided options in the current form we should set the value.
         $datahash = $options->getRowsHash();
         foreach ($datahash as $locator => $value) {
+            $field = behat_field_manager::get_form_field_from_label($locator, $this);
+            $field->set_value($value);
+        }
+    }
 
-            try {
-                $field = behat_field_manager::get_form_field_from_label($locator, $this);
-                $field->set_value($value);
+    /**
+     * Get the options specific to this step of the backup/restore process.
+     *
+     * @param TableNode $options The options table to filter
+     * @param string $step The name of the step
+     * @return TableNode The filtered options table
+     * @throws ExpectationException
+     */
+    protected function get_step_options($options, $step) {
+        // Nothing to fill if no options are provided.
+        if (!$options) {
+            return;
+        }
 
-            } catch (ElementNotFoundException $e) {
-                // Next provided option then, this one should be part of another page's fields.
+        $pageoptions = clone $options;
+
+        $rows = $options->getRows();
+        $newrows = array();
+        foreach ($rows as $k => $data) {
+            if (count($data) !== 3) {
+                // Not enough information to guess the page.
+                throw new ExpectationException("The backup/restore step must be specified for all backup options");
+            } else if ($data[0] == $step) {
+                unset($data[0]);
+                $newrows[] = $data;
             }
         }
+        $pageoptions->setRows($newrows);
+        return $pageoptions;
     }
 
+
     /**
      * Waits until the DOM and the page Javascript code is ready.
      *
index e871233..e5433f1 100644 (file)
@@ -25,7 +25,7 @@ Feature: Restore Moodle 2 course backups
   @javascript
   Scenario: Restore a course in another existing course
     When I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     And I restore "test_backup.mbz" backup into "Course 2" course using this options:
     Then I should see "Course 2"
     And I should see "Community finder" in the "Community finder" "block"
@@ -34,9 +34,9 @@ Feature: Restore Moodle 2 course backups
   @javascript
   Scenario: Restore a course in a new course
     When I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     And I restore "test_backup.mbz" backup into a new course using this options:
-      | Course name | Course 1 restored in a new course |
+      | Schema | Course name | Course 1 restored in a new course |
     Then I should see "Course 1 restored in a new course"
     And I should see "Community finder" in the "Community finder" "block"
     And I should see "Test forum name"
@@ -50,12 +50,12 @@ Feature: Restore Moodle 2 course backups
   @javascript
   Scenario: Restore a backup into the same course
     When I backup "Course 3" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     And I restore "test_backup.mbz" backup into "Course 2" course using this options:
-      | setting_section_section_3_included | 0 |
-      | setting_section_section_3_userinfo | 0 |
-      | setting_section_section_5_included | 0 |
-      | setting_section_section_5_userinfo | 0 |
+      | Schema | setting_section_section_3_included | 0 |
+      | Schema | setting_section_section_3_userinfo | 0 |
+      | Schema | setting_section_section_5_included | 0 |
+      | Schema | setting_section_section_5_userinfo | 0 |
     Then I should see "Course 2"
     And I should see "Test assign name"
     And I should not see "Test database name"
@@ -63,15 +63,15 @@ Feature: Restore Moodle 2 course backups
   @javascript
   Scenario: Restore a backup into the same course removing it's contents before that
     When I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     And I follow "Course 1"
     And I add a "Forum" to section "1" and I fill the form with:
       | Forum name | Test forum post backup name |
       | Description | Test forum post backup description |
     And I follow "Restore"
     And I merge "test_backup.mbz" backup into the current course after deleting it's contents using this options:
-      | setting_section_section_5_userinfo | 0 |
-      | setting_section_section_5_included | 0 |
+      | Schema | setting_section_section_8_userinfo | 0 |
+      | Schema | setting_section_section_8_included | 0 |
     Then I should see "Course 1"
     And I should not see "Section 3"
     And I should not see "Test forum post backup name"
@@ -81,7 +81,7 @@ Feature: Restore Moodle 2 course backups
   @javascript
   Scenario: Restore a backup into a new course changing the course format afterwards
     Given I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     When I restore "test_backup.mbz" backup into a new course using this options:
     Then I should see "Topic 1"
     And I should see "Test forum name"
@@ -117,9 +117,9 @@ Feature: Restore Moodle 2 course backups
     And I hide section "3"
     And I hide section "7"
     When I backup "Course 1" course using this options:
-      | Filename | test_backup.mbz |
+      | Confirmation | Filename | test_backup.mbz |
     And I restore "test_backup.mbz" backup into "Course 2" course using this options:
-      | Overwrite course configuration | Yes |
+      | Schema | Overwrite course configuration | Yes |
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
     Then the field "id_format" matches value "Topics format"
diff --git a/blocks/navigation/tests/behat/expand_courses_node.feature b/blocks/navigation/tests/behat/expand_courses_node.feature
new file mode 100644 (file)
index 0000000..2fd28ca
--- /dev/null
@@ -0,0 +1,190 @@
+@block @block_navigation
+Feature: Expand the courses nodes within the navigation block
+  In order to navigate the site
+  As an anonymous user, a guest, a student, and an admin
+  I need to expand the courses node in the navigation block and check the display of courses and categories.
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@local.host |
+      | student1 | Student | 1 | student1@local.host |
+    And the following "categories" exist:
+      | name   | category | idnumber | visible |
+      | cat1   | 0        | cat1     | 1       |
+      | cat2   | 0        | cat2     | 1       |
+      | cat21  | cat2     | cat21    | 1       |
+      | cat211 | cat21    | cat211   | 1       |
+      | cat3   | 0        | cat3     | 0       |
+    And the following "courses" exist:
+      | fullname  | shortname | category | visible |
+      | Course 1  | c1        | cat1     | 1       |
+      | Course 2  | c2        | cat2     | 1       |
+      | Course 3  | c3        | cat21    | 1       |
+      | Course 4  | c4        | cat211   | 1       |
+      | Course 5  | c5        | cat211   | 0       |
+      | Course 6  | c6        | cat211   | 0       |
+      | Course 7  | c7        | cat3     | 1       |
+      | Course 8  | c8        | cat3     | 0       |
+    And the following "course enrolments" exist:
+      | user     | course | role    |
+      | teacher1 | c1     | teacher |
+      | teacher1 | c3     | teacher |
+      | teacher1 | c5     | teacher |
+      | student1 | c1     | student |
+      | student1 | c2     | student |
+      | student1 | c4     | student |
+    And I log in as "admin"
+    And I follow "Course 2"
+    And I turn editing mode on
+    And I click on "Edit settings" "link" in the "Administration" "block"
+    And I set the following fields to these values:
+      | Allow guest access | Yes |
+    And I press "Save changes"
+    And I set the following administration settings values:
+      | Show all courses | 1 |
+    And I log out
+
+  @javascript
+  Scenario: As an anonymous user I expand the courses node to see courses.
+    When I should see "You are not logged in." in the ".logininfo" "css_element"
+    And I should see "Home" in the "Navigation" "block"
+    And I should see "Courses" in the "Navigation" "block"
+    And I expand "Courses" node
+    And I should see "cat1" in the "Navigation" "block"
+    And I should see "cat2" in the "Navigation" "block"
+    And I should not see "cat3" in the "Navigation" "block"
+    And I expand "cat1" node
+    And I expand "cat2" node
+    And I should see "cat21" in the "Navigation" "block"
+    And I expand "cat21" node
+    And I should see "cat211" in the "Navigation" "block"
+    And I expand "cat211" node
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should see "c3" in the "Navigation" "block"
+    And I should see "c4" in the "Navigation" "block"
+    And I should not see "c5" in the "Navigation" "block"
+    And I should not see "c6" in the "Navigation" "block"
+    And navigation node "c1" should not be expandable
+    And navigation node "c2" should not be expandable
+    And navigation node "c3" should not be expandable
+    And navigation node "c4" should not be expandable
+
+  @javascript
+  Scenario: As the admin user I expand the courses and category nodes to see courses.
+    When I log in as "admin"
+    And I should see "Home" in the "Navigation" "block"
+    And I should see "Courses" in the "Navigation" "block"
+    And I expand "Courses" node
+    And I should see "cat1" in the "Navigation" "block"
+    And I should see "cat2" in the "Navigation" "block"
+    And I should see "cat3" in the "Navigation" "block"
+    And I expand "cat1" node
+    And I expand "cat2" node
+    And I expand "cat3" node
+    And I should see "cat21" in the "Navigation" "block"
+    And I expand "cat21" node
+    And I should see "cat211" in the "Navigation" "block"
+    And I expand "cat211" node
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should see "c3" in the "Navigation" "block"
+    And I should see "c4" in the "Navigation" "block"
+    And I should see "c5" in the "Navigation" "block"
+    And I should see "c6" in the "Navigation" "block"
+    And I should see "c7" in the "Navigation" "block"
+    And I should see "c8" in the "Navigation" "block"
+    And navigation node "c1" should be expandable
+    And navigation node "c2" should be expandable
+    And navigation node "c3" should be expandable
+    And navigation node "c4" should be expandable
+    And navigation node "c5" should be expandable
+    And navigation node "c6" should be expandable
+    And navigation node "c7" should be expandable
+    And navigation node "c8" should be expandable
+
+  @javascript
+  Scenario: As teacher1 I expand the courses and category nodes to see courses.
+    When I log in as "teacher1"
+    And I should see "Home" in the "Navigation" "block"
+    And I should see "Courses" in the "Navigation" "block"
+    And I expand "Courses" node
+    And I should see "cat1" in the "Navigation" "block"
+    And I should see "cat2" in the "Navigation" "block"
+    And I should not see "cat3" in the "Navigation" "block"
+    And I expand "cat1" node
+    And I expand "cat2" node
+    And I should see "cat21" in the "Navigation" "block"
+    And I expand "cat21" node
+    And I should see "cat211" in the "Navigation" "block"
+    And I expand "cat211" node
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should see "c3" in the "Navigation" "block"
+    And I should see "c4" in the "Navigation" "block"
+    And I should see "c5" in the "Navigation" "block"
+    And I should not see "c6" in the "Navigation" "block"
+    And I should not see "c7" in the "Navigation" "block"
+    And I should not see "c8" in the "Navigation" "block"
+    And navigation node "c1" should be expandable
+    And navigation node "c2" should be expandable
+    And navigation node "c3" should be expandable
+    And navigation node "c4" should not be expandable
+    And navigation node "c5" should be expandable
+
+  @javascript
+  Scenario: As student1 I expand the courses and category nodes to see courses.
+    When I log in as "student1"
+    And I should see "Home" in the "Navigation" "block"
+    And I should see "Courses" in the "Navigation" "block"
+    And I expand "Courses" node
+    And I should see "cat1" in the "Navigation" "block"
+    And I should see "cat2" in the "Navigation" "block"
+    And I should not see "cat3" in the "Navigation" "block"
+    And I expand "cat1" node
+    And I expand "cat2" node
+    And I should see "cat21" in the "Navigation" "block"
+    And I expand "cat21" node
+    And I should see "cat211" in the "Navigation" "block"
+    And I expand "cat211" node
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should see "c3" in the "Navigation" "block"
+    And I should see "c4" in the "Navigation" "block"
+    And I should not see "c5" in the "Navigation" "block"
+    And I should not see "c6" in the "Navigation" "block"
+    And I should not see "c7" in the "Navigation" "block"
+    And I should not see "c8" in the "Navigation" "block"
+    And navigation node "c1" should be expandable
+    And navigation node "c2" should be expandable
+    And navigation node "c3" should not be expandable
+    And navigation node "c4" should be expandable
+
+  @javascript
+  Scenario: As guest I expand the courses and category nodes to see courses.
+    When I log in as "guest"
+    And I should see "Home" in the "Navigation" "block"
+    And I should see "Courses" in the "Navigation" "block"
+    And I expand "Courses" node
+    And I should see "cat1" in the "Navigation" "block"
+    And I should see "cat2" in the "Navigation" "block"
+    And I should not see "cat3" in the "Navigation" "block"
+    And I expand "cat1" node
+    And I expand "cat2" node
+    And I should see "cat21" in the "Navigation" "block"
+    And I expand "cat21" node
+    And I should see "cat211" in the "Navigation" "block"
+    And I expand "cat211" node
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should see "c3" in the "Navigation" "block"
+    And I should see "c4" in the "Navigation" "block"
+    And I should not see "c5" in the "Navigation" "block"
+    And I should not see "c6" in the "Navigation" "block"
+    And I should not see "c7" in the "Navigation" "block"
+    And I should not see "c8" in the "Navigation" "block"
+    And navigation node "c1" should not be expandable
+    And navigation node "c2" should be expandable
+    And navigation node "c3" should not be expandable
+    And navigation node "c4" should not be expandable
\ No newline at end of file
diff --git a/blocks/navigation/tests/behat/expand_my_courses_setting.feature b/blocks/navigation/tests/behat/expand_my_courses_setting.feature
new file mode 100644 (file)
index 0000000..79eeb0e
--- /dev/null
@@ -0,0 +1,56 @@
+@block @block_navigation
+Feature: Test expand my courses navigation setting
+  As a student
+  I visit my My Moodle page and observe the the My Courses branch
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email |
+      | student1 | Student | 1 | student1@asd.com |
+    And the following "categories" exist:
+      | name  | category | idnumber |
+      | cat1  | 0        | cat1     |
+    And the following "courses" exist:
+      | fullname | shortname | category |
+      | Course1  | c1        | cat1     |
+      | Course2  | c2        | cat1     |
+      | Course3  | c3        | cat1     |
+    And the following "course enrolments" exist:
+      | user     | course | role    |
+      | student1 | c1     | student |
+      | student1 | c2    | student |
+
+  Scenario: The My Courses branch is expanded on the My Moodle page by default
+    When I log in as "student1"
+    And I follow "My home"
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should not see "c3" in the "Navigation" "block"
+
+  @javascript
+  Scenario: The My Courses branch is collapsed when expand my courses is off
+    Given I log in as "admin"
+    And I set the following administration settings values:
+      | Expand My Courses initially on My Moodle page | 0 |
+    And I log out
+    Given I log in as "student1"
+    And I follow "My home"
+    Then I should not see "c1" in the "Navigation" "block"
+    And I should not see "c2" in the "Navigation" "block"
+    And I should not see "c3" in the "Navigation" "block"
+
+  @javascript
+  Scenario: My Courses can be expanded on the My Moodle page when expand my courses is off
+    Given I log in as "admin"
+    And I set the following administration settings values:
+      | Expand My Courses initially on My Moodle page | 0 |
+    And I log out
+    Given I log in as "student1"
+    And I follow "My home"
+    And I should not see "c1" in the "Navigation" "block"
+    And I should not see "c2" in the "Navigation" "block"
+    And I should not see "c3" in the "Navigation" "block"
+    And I expand "My courses" node
+    Then I should see "c1" in the "Navigation" "block"
+    And I should see "c2" in the "Navigation" "block"
+    And I should not see "c3" in the "Navigation" "block"
\ No newline at end of file
index 4c8ad1b..a629d9e 100644 (file)
@@ -87,8 +87,6 @@ Feature: View my courses in navigation block
     When I expand "cat3" node
     And I expand "cat31" node
     And I expand "cat1" node
-    And I should see "c1" in the "Navigation" "block"
-    And I expand "c1" node
     Then I should see "cat1" in the "Navigation" "block"
     And I should see "cat2" in the "Navigation" "block"
     And I should see "cat3" in the "Navigation" "block"
index 6aa17f3..b6f5246 100644 (file)
@@ -8,6 +8,6 @@
     "require-dev": {
         "phpunit/phpunit": "3.7.*",
         "phpunit/dbUnit": "1.2.*",
-        "moodlehq/behat-extension": "1.28.3"
+        "moodlehq/behat-extension": "1.28.4"
     }
 }
index ea619fe..622909b 100644 (file)
@@ -16,7 +16,6 @@ class course_reset_form extends moodleform {
         $mform->addElement('date_selector', 'reset_start_date', get_string('startdate'), array('optional'=>true));
         $mform->addHelpButton('reset_start_date', 'startdate');
         $mform->addElement('checkbox', 'reset_events', get_string('deleteevents', 'calendar'));
-        $mform->addElement('checkbox', 'reset_logs', get_string('deletelogs'));
         $mform->addElement('checkbox', 'reset_notes', get_string('deletenotes', 'notes'));
         $mform->addElement('checkbox', 'reset_comments', get_string('deleteallcomments', 'moodle'));
         $mform->addElement('checkbox', 'reset_completion', get_string('deletecompletiondata', 'completion'));
@@ -105,7 +104,7 @@ class course_reset_form extends moodleform {
 
         $mform =& $this->_form;
 
-        $defaults = array ('reset_events'=>1, 'reset_logs'=>1, 'reset_roles_local'=>1, 'reset_gradebook_grades'=>1, 'reset_notes'=>1);
+        $defaults = array ('reset_events'=>1, 'reset_roles_local'=>1, 'reset_gradebook_grades'=>1, 'reset_notes'=>1);
 
         // Set student as default in unenrol user list, if role with student archetype exist.
         if ($studentrole = get_archetype_roles('student')) {
index a645544..0715544 100644 (file)
@@ -64,13 +64,13 @@ Feature: Users can auto-enrol themself in courses where self enrolment is allowe
     And I press "Create group"
     And I set the following fields to these values:
       | Group name | Group 1 |
-      | Enrolment key | testgroupenrolkey |
+      | Enrolment key | Test-groupenrolkey1 |
     And I press "Save changes"
     And I log out
     And I log in as "student1"
     And I follow "Course 1"
     And I set the following fields to these values:
-      | Enrolment key | testgroupenrolkey |
+      | Enrolment key | Test-groupenrolkey1 |
     And I press "Enrol me"
     Then I should see "Topic 1"
     And I should not see "Enrolment options"
index 5c351fa..9569517 100644 (file)
@@ -205,7 +205,7 @@ class gradingform_guide_controller extends gradingform_controller {
         } else {
             $newcomment = $newdefinition->guide['comments']; // New ones to be saved.
         }
-        $currentcomments = $currentdefinition->guide_comment;
+        $currentcomments = $currentdefinition->guide_comments;
         $commentfields = array('sortorder', 'description');
         foreach ($newcomment as $id => $comment) {
             if (preg_match('/^NEWID\d+$/', $id)) {
@@ -309,7 +309,7 @@ class gradingform_guide_controller extends gradingform_controller {
         $this->definition = $definition;
         // Now get criteria.
         $this->definition->guide_criteria = array();
-        $this->definition->guide_comment = array();
+        $this->definition->guide_comments = array();
         $criteria = $DB->get_recordset('gradingform_guide_criteria', array('definitionid' => $this->definition->id), 'sortorder');
         foreach ($criteria as $criterion) {
             foreach (array('id', 'sortorder', 'description', 'descriptionformat',
@@ -327,7 +327,7 @@ class gradingform_guide_controller extends gradingform_controller {
         $comments = $DB->get_recordset('gradingform_guide_comments', array('definitionid' => $this->definition->id), 'sortorder');
         foreach ($comments as $comment) {
             foreach (array('id', 'sortorder', 'description', 'descriptionformat') as $fieldname) {
-                $this->definition->guide_comment[$comment->id][$fieldname] = $comment->{$fieldname};
+                $this->definition->guide_comments[$comment->id][$fieldname] = $comment->{$fieldname};
             }
         }
         $comments->close();
@@ -404,8 +404,8 @@ class gradingform_guide_controller extends gradingform_controller {
         } else if (!$definition && $addemptycriterion) {
             $properties->guide['criteria'] = array('addcriterion' => 1);
         }
-        if (!empty($definition->guide_comment)) {
-            $properties->guide['comments'] = $definition->guide_comment;
+        if (!empty($definition->guide_comments)) {
+            $properties->guide['comments'] = $definition->guide_comments;
         } else if (!$definition && $addemptycriterion) {
             $properties->guide['comments'] = array('addcomment' => 1);
         }
@@ -508,7 +508,7 @@ class gradingform_guide_controller extends gradingform_controller {
         }
 
         $criteria = $this->definition->guide_criteria;
-        $comments = $this->definition->guide_comment;
+        $comments = $this->definition->guide_comments;
         $output = $this->get_renderer($page);
 
         $guide = '';
@@ -652,7 +652,7 @@ class gradingform_guide_controller extends gradingform_controller {
 
     /**
      * @return array An array containing 2 key/value pairs which hold the external_multiple_structure
-     * for the 'guide_criteria' and the 'guide_comment'.
+     * for the 'guide_criteria' and the 'guide_comments'.
      * @see gradingform_controller::get_external_definition_details()
      * @since Moodle 2.5
      */
@@ -660,8 +660,8 @@ class gradingform_guide_controller extends gradingform_controller {
         $guide_criteria = new external_multiple_structure(
                               new external_single_structure(
                                   array(
-                                      'id'   => new external_value(PARAM_INT, 'criterion id'),
-                                      'sortorder' => new external_value(PARAM_INT, 'sortorder'),
+                                      'id'   => new external_value(PARAM_INT, 'criterion id', VALUE_OPTIONAL),
+                                      'sortorder' => new external_value(PARAM_INT, 'sortorder', VALUE_OPTIONAL),
                                       'description' => new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL),
                                       'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
                                       'shortname' => new external_value(PARAM_TEXT, 'description'),
@@ -671,17 +671,17 @@ class gradingform_guide_controller extends gradingform_controller {
                                       )
                                   )
         );
-        $guide_comment = new external_multiple_structure(
+        $guide_comments = new external_multiple_structure(
                               new external_single_structure(
                                   array(
-                                      'id'   => new external_value(PARAM_INT, 'criterion id'),
-                                      'sortorder' => new external_value(PARAM_INT, 'sortorder'),
+                                      'id'   => new external_value(PARAM_INT, 'criterion id', VALUE_OPTIONAL),
+                                      'sortorder' => new external_value(PARAM_INT, 'sortorder', VALUE_OPTIONAL),
                                       'description' => new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL),
                                       'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL)
                                    )
                               ), 'comments', VALUE_OPTIONAL
         );
-        return array('guide_criteria' => $guide_criteria, 'guide_comment' => $guide_comment);
+        return array('guide_criteria' => $guide_criteria, 'guide_comments' => $guide_comments);
     }
 
     /**
@@ -895,7 +895,7 @@ class gradingform_guide_instance extends gradingform_instance {
             }
         }
         $criteria = $this->get_controller()->get_definition()->guide_criteria;
-        $comments = $this->get_controller()->get_definition()->guide_comment;
+        $comments = $this->get_controller()->get_definition()->guide_comments;
         $options = $this->get_controller()->get_options();
         $value = $gradingformelement->getValue();
         $html = '';
index 1cf9b44..36d366d 100644 (file)
@@ -668,15 +668,15 @@ class gradingform_rubric_controller extends gradingform_controller {
         $rubric_criteria = new external_multiple_structure(
             new external_single_structure(
                 array(
-                   'id'   => new external_value(PARAM_INT, 'criterion id'),
-                   'sortorder' => new external_value(PARAM_INT, 'sortorder'),
+                   'id'   => new external_value(PARAM_INT, 'criterion id', VALUE_OPTIONAL),
+                   'sortorder' => new external_value(PARAM_INT, 'sortorder', VALUE_OPTIONAL),
                    'description' => new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL),
                    'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
                    'levels' => new external_multiple_structure(
                                    new external_single_structure(
                                        array(
-                                        'id' => new external_value(PARAM_INT, 'level id'),
-                                        'score' => new external_value(PARAM_FLOAT, 'score'),
+                                        'id' => new external_value(PARAM_INT, 'level id', VALUE_OPTIONAL),
+                                        'score' => new external_value(PARAM_FLOAT, 'score', VALUE_OPTIONAL),
                                         'definition' => new external_value(PARAM_RAW, 'definition', VALUE_OPTIONAL),
                                         'definitionformat' => new external_format_value('definition', VALUE_OPTIONAL)
                                        )
index 06fe060..0a0bf07 100644 (file)
@@ -138,8 +138,8 @@ class behat_grading extends behat_base {
         // Should work with both templates and own forms.
         $literaltemplate = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('templatepick', 'grading'));
         $literalownform = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('templatepickownform', 'grading'));
-        $usetemplatexpath = "//a[./descendant::div[text()=$literaltemplate]]|" .
-            "//a[./descendant::div[text()=$literalownform]]";
+        $usetemplatexpath = "/a[./descendant::div[text()=$literaltemplate]]|" .
+            "/a[./descendant::div[text()=$literalownform]]";
 
         return array(
             new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
index 72e6849..c29947a 100644 (file)
@@ -111,11 +111,19 @@ class group_form extends moodleform {
                 }
             }
 
-            if (!empty($CFG->groupenrolmentkeypolicy) and $data['enrolmentkey'] != '' and $group->enrolmentkey !== $data['enrolmentkey']) {
-                // enforce password policy only if changing password
+            if ($data['enrolmentkey'] != '') {
                 $errmsg = '';
-                if (!check_password_policy($data['enrolmentkey'], $errmsg)) {
+                if (!empty($CFG->groupenrolmentkeypolicy) && $group->enrolmentkey !== $data['enrolmentkey']
+                        && !check_password_policy($data['enrolmentkey'], $errmsg)) {
+                    // Enforce password policy when the password is changed.
                     $errors['enrolmentkey'] = $errmsg;
+                } else {
+                    // Prevent twice the same enrolment key in course groups.
+                    $sql = "SELECT id FROM {groups} WHERE id <> :groupid AND courseid = :courseid AND enrolmentkey = :key";
+                    $params = array('groupid' => $data['id'], 'courseid' => $COURSE->id, 'key' => $data['enrolmentkey']);
+                    if ($DB->record_exists_sql($sql, $params)) {
+                        $errors['enrolmentkey'] = get_string('enrolmentkeyalreadyinuse', 'group');
+                    }
                 }
             }
 
@@ -123,6 +131,15 @@ class group_form extends moodleform {
             $errors['name'] = get_string('groupnameexists', 'group', $name);
         } else if (!empty($idnumber) && groups_get_group_by_idnumber($COURSE->id, $idnumber)) {
             $errors['idnumber']= get_string('idnumbertaken');
+        } else if ($data['enrolmentkey'] != '') {
+            $errmsg = '';
+            if (!empty($CFG->groupenrolmentkeypolicy) && !check_password_policy($data['enrolmentkey'], $errmsg)) {
+                // Enforce password policy.
+                $errors['enrolmentkey'] = $errmsg;
+            } else if ($DB->record_exists('groups', array('courseid' => $COURSE->id, 'enrolmentkey' => $data['enrolmentkey']))) {
+                // Prevent the same enrolment key from being used multiple times in course groups.
+                $errors['enrolmentkey'] = get_string('enrolmentkeyalreadyinuse', 'group');
+            }
         }
 
         return $errors;
index 51970ed..466b7d4 100644 (file)
@@ -89,3 +89,42 @@ Feature: Organize students into groups
       | Grouping name | Not the greatest grouping, but it's ok! |
     And I press "Save changes"
     And I should see "Not the greatest grouping, but it's ok!"
+
+  Scenario: Create groups with enrolment key
+    Given the following "courses" exist:
+      | fullname | shortname | category | groupmode |
+      | Course 1 | C1 | 0 | 1 |
+      | Course 2 | C2 | 0 | 1 |
+    And I log in as "admin"
+    And I follow "Course 1"
+    And I expand "Users" node
+    And I follow "Groups"
+    When I press "Create group"
+    And I set the following fields to these values:
+      | Group name | Group A |
+      | Enrolment key | badpasswd |
+    And I press "Save changes"
+    And I should see "Passwords must have at least 1 digit(s)"
+    And I set the following fields to these values:
+      | Group name | Group A |
+      | Enrolment key | Abcdef-1 |
+    And I press "Save changes"
+    And I press "Create group"
+    And I set the following fields to these values:
+      | Group name | Group B |
+      | Enrolment key | Abcdef-1 |
+    And I press "Save changes"
+    Then I should see "This enrolment key is already used for another group."
+    And I set the following fields to these values:
+      | Enrolment key | Abcdef-2 |
+    And I press "Save changes"
+    And the "groups" select box should contain "Group B (0)"
+    And I am on homepage
+    And I follow "Course 2"
+    And I expand "Users" node
+    And I follow "Groups"
+    And I press "Create group"
+    And I set the following fields to these values:
+      | Group name | Group A |
+      | Enrolment key | Abcdef-1 |
+    And I should not see "This enrolment key is already used for another group."
\ No newline at end of file
index a363d2b..66e1920 100644 (file)
@@ -100,3 +100,54 @@ Feature: Automatic updating of groups and groupings
     And the "idnumber" "field" should be readonly
     And the field "idnumber" matches value "An ID"
 
+  @javascript
+  Scenario: Update groups with enrolment key
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 2 | C2 |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C2 | editingteacher |
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I expand "Users" node
+    And I follow "Groups"
+    And I set the field "groups" to "Group (with ID)"
+    And I press "Edit group settings"
+    And I set the following fields to these values:
+      | Enrolment key | badpasswd |
+    When I press "Save changes"
+    Then I should see "Passwords must have at least 1 digit(s)"
+    And I set the following fields to these values:
+      | Enrolment key | Abcdef-1 |
+    And I press "Save changes"
+    And I set the field "groups" to "Group (with ID)"
+    And I press "Edit group settings"
+    And I press "Save changes"
+    And I should not see "This enrolment key is already used for another group."
+    And I set the field "groups" to "Group (without ID)"
+    And I press "Edit group settings"
+    And I set the following fields to these values:
+      | Enrolment key | Abcdef-1 |
+    And I press "Save changes"
+    And I should see "This enrolment key is already used for another group."
+    And I set the following fields to these values:
+      | Enrolment key | Abcdef-2 |
+    And I press "Save changes"
+    And I should not see "This enrolment key is already used for another group."
+    And I am on homepage
+    And I follow "Course 2"
+    And I expand "Users" node
+    And I follow "Groups"
+    And I press "Create group"
+    And I set the following fields to these values:
+      | Group name | Group A |
+    And I press "Save changes"
+    And I should not see "This enrolment key is already used for another group."
+    And I set the field "groups" to "Group A"
+    And I press "Edit group settings"
+    And I set the following fields to these values:
+      | Enrolment key | Abcdef-1 |
+    And I press "Save changes"
+    And I should not see "This enrolment key is already used for another group."
\ No newline at end of file
index 62734b5..4e9db3d 100644 (file)
@@ -732,6 +732,8 @@ $string['navadduserpostslinks'] = 'Add links to view user posts';
 $string['navadduserpostslinks_help'] = 'If enabled two links will be added to each user in the navigation to view discussions the user has started and posts the user has made in forums throughout the site or in specific courses.';
 $string['navigationupgrade'] = 'This upgrade introduces two new navigation blocks that will replace these blocks: Administration, Courses, Activities and Participants.  If you had set any special permissions on those blocks you should check to make sure everything is behaving as you want it.';
 $string['navcourselimit'] = 'Course limit';
+$string['navexpandmycourses'] = 'Expand My Courses initially on My Moodle page';
+$string['navexpandmycourses_desc'] = 'When enabled the My Courses branch will be expanded initially on the My Moodle page and any pages within the My Moodle area.';
 $string['navshowfullcoursenames'] = 'Show course full names';
 $string['navshowfullcoursenames_help'] = 'If enabled courses in the navigation will be shown with using their full name rather than their short name.';
 $string['navshowfrontpagemods'] = 'Show front page activities in the navigation';
index 6d4eab7..6037469 100644 (file)
@@ -51,6 +51,7 @@ $string['cachedef_groupdata'] = 'Course group information';
 $string['cachedef_htmlpurifier'] = 'HTML Purifier - cleaned content';
 $string['cachedef_langmenu'] = 'List of available languages';
 $string['cachedef_locking'] = 'Locking';
+$string['cachedef_navigation_expandcourse'] = 'Navigation expandable courses';
 $string['cachedef_observers'] = 'Event observers';
 $string['cachedef_plugin_manager'] = 'Plugin info manager';
 $string['cachedef_questiondata'] = 'Question definitions';
index b5c1c3c..7bef32a 100644 (file)
@@ -489,7 +489,7 @@ $string['statsnodata'] = 'There is no available data for that combination of cou
 $string['storedfilecannotcreatefile'] = 'Can not create local file pool file, please verify permissions in dataroot and available disk space.';
 $string['storedfilecannotcreatefiledirs'] = 'Can not create local file pool directories, please verify permissions in dataroot.';
 $string['storedfilecannotread'] = 'Can not read file, either file does not exist or there are permission problems';
-$string['storedfilenotcreated'] = 'Can not create file "{$a->contextid}/{$a->component}/{$a->filearea}/{$a->itemid}/{$a->filepath}/{$a->filename}"';
+$string['storedfilenotcreated'] = 'Can not create file "{$a->contextid}/{$a->component}/{$a->filearea}/{$a->itemid}{$a->filepath}{$a->filename}"';
 $string['storedfileproblem'] = 'Unknown exception related to local files ({$a})';
 $string['tagdisabled'] = 'Tags are disabled!';
 $string['tagnotfound'] = 'The specified tag was not found in the database';
index 7fee0f7..ac1316a 100644 (file)
@@ -61,6 +61,7 @@ $string['enrolmentkey'] = 'Enrolment key';
 $string['enrolmentkey_help'] = 'An enrolment key enables access to the course to be restricted to only those who know the key. If a group enrolment key is specified, then not only will entering that key let the user into the course, but it will also automatically make them a member of this group.
 
 Note: Group enrolment keys must be enabled in the self enrolment settings and an enrolment key for the course must also be specified.';
+$string['enrolmentkeyalreadyinuse'] = 'This enrolment key is already used for another group.';
 $string['erroraddremoveuser'] = 'Error adding/removing user {$a} to group';
 $string['erroreditgroup'] = 'Error creating/updating group {$a}';
 $string['erroreditgrouping'] = 'Error creating/updating grouping {$a}';
index b4e181f..2b46997 100644 (file)
@@ -478,7 +478,6 @@ $string['deletechecktypename'] = 'Are you sure that you want to delete the {$a->
 $string['deletecheckfiles'] = 'Are you absolutely sure you want to delete these files?';
 $string['deletecheckfull'] = 'Are you absolutely sure you want to completely delete {$a} ?';
 $string['deletecheckwarning'] = 'You are about to delete these files';
-$string['deletelogs'] = 'Delete logs';
 $string['deleteselected'] = 'Delete selected';
 $string['deleteselectedkey'] = 'Delete selected key';
 $string['deletingcourse'] = 'Deleting {$a}';
index e666b02..8f3ff72 100644 (file)
@@ -65,6 +65,32 @@ editor.set("value", "' . $value . '");
         }
     }
 
+    /**
+     * Select all the text in the form field.
+     *
+     */
+    public function select_text() {
+        // NodeElement.keyPress simply doesn't work.
+        if (!$this->running_javascript()) {
+            throw new coding_exception('Selecting text requires javascript.');
+        }
+
+        $editorid = $this->field->getAttribute('id');
+        $js = ' (function() {
+    var e = document.getElementById("'.$editorid.'editable"),
+        r = rangy.createRange(),
+        s = rangy.getSelection();
+
+    while ((e.firstChild !== null) && (e.firstChild.nodeType != document.TEXT_NODE)) {
+        e = e.firstChild;
+    }
+    e.focus();
+    r.selectNodeContents(e);
+    s.setSingleRange(r);
+}()); ';
+        $this->session->executeScript($js);
+    }
+
     /**
      * Matches the provided value against the current field value.
      *
index c1abb1a..d97ce53 100644 (file)
@@ -235,5 +235,4 @@ class behat_form_field {
 
         return $this->fieldlocator;
     }
-
 }
index 5b50384..4f11edb 100644 (file)
@@ -41,6 +41,35 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class message_sent extends base {
+    /**
+     * Create event using ids.
+     * @param int $userfromid
+     * @param int $usertoid
+     * @param int $messageid
+     * @return message_sent
+     */
+    public static function create_from_ids($userfromid, $usertoid, $messageid) {
+        // We may be sending a message from the 'noreply' address, which means we are not actually sending a
+        // message from a valid user. In this case, we will set the userid to 0.
+        // Check if the userid is valid.
+        if (!\core_user::is_real_user($userfromid)) {
+            $userfromid = 0;
+        }
+
+        $event = self::create(array(
+            'userid' => $userfromid,
+            'context' => \context_system::instance(),
+            'relateduserid' => $usertoid,
+            'other' => array(
+                // In earlier versions it can either be the id in the 'message_read' or 'message' table.
+                // Now it is always the id from 'message' table. Please note that the record is still moved
+                // to the 'message_read' table later when message marked as read.
+                'messageid' => $messageid
+            )
+        ));
+
+        return $event;
+    }
 
     /**
      * Init method.
index eec49ce..9e9ecd1 100644 (file)
@@ -226,6 +226,7 @@ class core_grading_external extends external_api {
                 'cmid'    => new external_value(PARAM_INT, 'course module id'),
                 'contextid'  => new external_value(PARAM_INT, 'context id'),
                 'component' => new external_value(PARAM_TEXT, 'component name'),
+                'areaname' => new external_value(PARAM_TEXT, 'area name'),
                 'activemethod' => new external_value(PARAM_TEXT, 'active method', VALUE_OPTIONAL),
                 'definitions'  => new external_multiple_structure(self::definition(), 'definitions')
             )
@@ -240,11 +241,11 @@ class core_grading_external extends external_api {
     private static function definition() {
         global $CFG;
         $definition = array();
-        $definition['id']                = new external_value(PARAM_INT, 'definition id');
+        $definition['id']                = new external_value(PARAM_INT, 'definition id', VALUE_OPTIONAL);
         $definition['method']            = new external_value(PARAM_TEXT, 'method');
         $definition['name']              = new external_value(PARAM_TEXT, 'name');
-        $definition['description']       = new external_value(PARAM_RAW, 'description');
-        $definition['descriptionformat'] = new external_format_value('description');
+        $definition['description']       = new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL);
+        $definition['descriptionformat'] = new external_format_value('description', VALUE_OPTIONAL);
         $definition['status']            = new external_value(PARAM_INT, 'status');
         $definition['copiedfromid']      = new external_value(PARAM_INT, 'copied from id', VALUE_OPTIONAL);
         $definition['timecreated']       = new external_value(PARAM_INT, 'creation time');
@@ -442,4 +443,140 @@ class core_grading_external extends external_api {
         );
     }
 
+    /**
+     * Describes the parameters for save_definitions
+     *
+     * @return external_function_parameters
+     * @since Moodle 2.8
+     */
+    public static function save_definitions_parameters() {
+        return new external_function_parameters(
+            array(
+                'areas' => new external_multiple_structure(self::grading_area(), 'areas with definitions to save')
+            )
+        );
+    }
+
+    /**
+     * Saves the areas and definitions
+     * @param array $areas array of areas containing definitions to be saved
+     * @return null
+     * @throws invalid_parameter_exception
+     * @since Moodle 2.8
+     */
+    public static function save_definitions($areas) {
+        $params = self::validate_parameters(self::save_definitions_parameters(),
+                                            array('areas' => $areas));
+
+        foreach ($params['areas'] as $area) {
+
+            $context = context::instance_by_id($area['contextid']);
+            require_capability('moodle/grade:managegradingforms', $context);
+            $gradingmanager = get_grading_manager($context, $area['component'], $area['areaname']);
+            $gradingmanager->set_active_method($area['activemethod']);
+            $availablemethods = $gradingmanager->get_available_methods();
+
+            foreach ($area['definitions'] as $definition) {
+                if (array_key_exists($definition['method'], $availablemethods)) {
+                    $controller = $gradingmanager->get_controller($definition['method']);
+                    $controller->update_definition(self::create_definition_object($definition));
+                } else {
+                    throw new invalid_parameter_exception('Unknown Grading method: '. $definition['method']);
+                }
+            }
+        }
+    }
+
+    /**
+     * Describes the return value for save_definitions
+     *
+     * @return external_single_structure
+     * @since Moodle 2.8
+     */
+    public static function save_definitions_returns() {
+        return null;
+    }
+
+    /**
+     * Creates a definition stdClass object using the values from the definition
+     * array that is passed in as a parameter
+     *
+     * @param array $definition
+     * @return stdClass definition object
+     * @since Moodle 2.8
+     */
+    private static function create_definition_object($definition) {
+        global $CFG;
+
+        $method = $definition['method'];
+        $definitionobject = new stdClass();
+        foreach ($definition as $key => $value) {
+            if (!is_array($value)) {
+                $definitionobject->$key = $value;
+            }
+        }
+        $text = '';
+        $format = FORMAT_MOODLE;
+        if (isset($definition['description'])) {
+            $text = $definition['description'];
+            if (isset($definition['descriptionformat'])) {
+                $format = $definition['descriptionformat'];
+            }
+        }
+        $definitionobject->description_editor = array('text' => $text, 'format' => $format);
+
+        require_once("$CFG->libdir/filelib.php");
+        require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
+        $details  = call_user_func('gradingform_'.$method.'_controller::get_external_definition_details');
+        $methodarray = array();
+        foreach (array_keys($details) as $definitionkey) {
+            $items = array();
+            $idnumber = 1;
+            foreach ($definition[$method][$definitionkey] as $item) {
+                $processeditem = self::set_new_ids($item, $idnumber);
+                $items[$processeditem['id']] = $processeditem;
+                $idnumber++;
+            }
+            $definitionobjectkey = substr($definitionkey, strlen($method.'_'));
+            $methodarray[$definitionobjectkey] = $items;
+            $definitionobject->$method = $methodarray;
+        }
+
+        return $definitionobject;
+    }
+
+    /**
+     * Recursively iterates through arrays. Any array without an id key-value combination
+     * is assumed to be an array of values to be inserted and an id key-value is added with
+     * the value matching the regex '/^NEWID\d+$/' that is expected by each grading form implementation.
+     *
+     * @param array $arraytoset the array to be processed
+     * @param int $startnumber the starting number for the new id numbers
+     * @return array with missing id keys added for all arrays
+     * @since Moodle 2.8
+     */
+    private static function set_new_ids($arraytoset, $startnumber) {
+        $result = array();
+        $foundid = false;
+        $number = $startnumber;
+        foreach ($arraytoset as $key1 => $value1) {
+            if (is_array($value1)) {
+                foreach ($value1 as $key2 => $value2) {
+                    $processedvalue = self::set_new_ids($value2, $number);
+                    $result[$key1][$processedvalue['id']] = $processedvalue;
+                    $number++;
+                }
+            } else {
+                $result[$key1] = $value1;
+            }
+            if ($key1 === 'id') {
+                $foundid = true;
+            }
+        }
+        if (!$foundid) {
+            $result['id'] = 'NEWID'.$number;
+        }
+        return $result;
+    }
+
 }
diff --git a/lib/classes/message/manager.php b/lib/classes/message/manager.php
new file mode 100644 (file)
index 0000000..c016bd5
--- /dev/null
@@ -0,0 +1,184 @@
+<?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/>.
+
+/**
+ * New messaging manager class.
+ *
+ * @package   core_message
+ * @since     Moodle 2.8
+ * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author    Petr Skoda <petr.skoda@totaralms.com>
+ */
+
+namespace core\message;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class used for various messaging related stuff.
+ *
+ * Note: Do NOT use directly in your code, it is intended to be used from core code only.
+ *
+ * @access private
+ *
+ * @package   core_message
+ * @since     Moodle 2.8
+ * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author    Petr Skoda <petr.skoda@totaralms.com>
+ */
+class manager {
+    /** @var array buffer of pending messages */
+    protected static $buffer = array();
+
+    /**
+     * Do the message sending.
+     *
+     * NOTE: to be used from message_send() only.
+     *
+     * @param \stdClass $eventdata fully prepared event data for processors
+     * @param \stdClass $savemessage the message saved in 'message' table
+     * @param array $processorlist list of processors for target user
+     * @return int $messageid the id from 'message' or 'message_read' table (false is not returned)
+     */
+    public static function send_message(\stdClass $eventdata, \stdClass $savemessage, array $processorlist) {
+        global $CFG;
+        require_once($CFG->dirroot.'/message/lib.php'); // This is most probably already included from messagelib.php file.
+
+        if (empty($processorlist)) {
+            // Trigger event for sending a message - we need to do this before marking as read!
+            \core\event\message_sent::create_from_ids($eventdata->userfrom->id, $eventdata->userto->id, $savemessage->id)->trigger();
+
+            if ($savemessage->notification or empty($CFG->messaging)) {
+                // If they have deselected all processors and its a notification mark it read. The user doesn't want to be bothered.
+                // The same goes if the messaging is completely disabled.
+                // We cannot insert directly to the message_read table because we want to get all events in proper order!
+                $messageid = message_mark_message_read($savemessage, time(), true);
+
+            } else {
+                // Just add it to the list of unread messages, there is no way it could be delivered to them,
+                // but they can read it via the messaging UI later.
+                $messageid = $savemessage->id;
+            }
+
+            return $messageid;
+        }
+
+        // Let the manager do the sending or buffering when db transaction in progress.
+        return self::send_message_to_processors($eventdata, $savemessage, $processorlist);
+    }
+
+    /**
+     * Send message to message processors.
+     *
+     * @param \stdClass $eventdata
+     * @param \stdClass $savemessage
+     * @param array $processorlist
+     * @return int $messageid
+     */
+    protected static function send_message_to_processors(\stdClass $eventdata, \stdClass $savemessage, array $processorlist) {
+        global $CFG, $DB;
+
+        // We cannot communicate with external systems in DB transactions,
+        // buffer the messages if necessary.
+
+        if ($DB->is_transaction_started()) {
+            // We need to clone all objects so that devs may not modify it from outside later.
+            $eventdata = clone($eventdata);
+            $eventdata->userto = clone($eventdata->userto);
+            $eventdata->userfrom = clone($eventdata->userfrom);
+
+            // Conserve some memory the same was as $USER setup does.
+            unset($eventdata->userto->description);
+            unset($eventdata->userfrom->description);
+
+            self::$buffer[] = array($eventdata, $savemessage, $processorlist);
+            return $savemessage->id;
+        }
+
+        $processors = get_message_processors(true);
+
+        $failed = false;
+        foreach ($processorlist as $procname) {
+            if (!$processors[$procname]->object->send_message($eventdata)) {
+                debugging('Error calling message processor ' . $procname);
+                $failed = true;
+                // Previously the $messageid = false here was overridden
+                // by other processors and message_mark_message_read() below.
+            }
+        }
+
+        // Trigger event for sending a message - must be done before marking as read.
+        \core\event\message_sent::create_from_ids($eventdata->userfrom->id, $eventdata->userto->id, $savemessage->id)->trigger();
+
+        if (empty($CFG->messaging)) {
+            // If messaging is disabled and they previously had forum notifications handled by the popup processor
+            // or any processor that puts a row in message_working then the notification will remain forever
+            // unread. To prevent this mark the message read if messaging is disabled.
+            $messageid = message_mark_message_read($savemessage, time());
+
+        } else if ($failed) {
+            // Something failed, better keep it as unread then.
+            $messageid = $savemessage->id;
+
+        } else if ($DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0) {
+            // If there is no more processors that want to process this we can move message to message_read.
+            $messageid = message_mark_message_read($savemessage, time(), true);
+
+        } else {
+            // Some processor is still working on the data, let's keep it unread.
+            $messageid = $savemessage->id;
+        }
+
+        return $messageid;
+    }
+
+    /**
+     * Notification from DML layer.
+     *
+     * Note: to be used from DML layer only.
+     */
+    public static function database_transaction_commited() {
+        if (!self::$buffer) {
+            return;
+        }
+        self::process_buffer();
+    }
+
+    /**
+     * Notification from DML layer.
+     *
+     * Note: to be used from DML layer only.
+     */
+    public static function database_transaction_rolledback() {
+        self::$buffer = array();
+    }
+
+    /**
+     * Sent out any buffered messages if necessary.
+     */
+    protected static function process_buffer() {
+        // Reset the buffer first in case we get exception from processor.
+        $messages = self::$buffer;
+        self::$buffer = array();
+
+        foreach ($messages as $message) {
+            list($eventdata, $savemessage, $processorlist) = $message;
+            self::send_message_to_processors($eventdata, $savemessage, $processorlist);
+        }
+    }
+}
index f7fb050..47e93c7 100644 (file)
@@ -340,7 +340,7 @@ abstract class scheduled_task extends task_base {
         // otherwise - choose the soonest (see man 5 cron).
         if ($this->dayofweek == '*') {
             $daysincrement = $daysincrementbymonth;
-        } else if ($this->dayofmonth == '*') {
+        } else if ($this->day == '*') {
             $daysincrement = $daysincrementbyweek;
         } else {
             // Take the smaller increment of days by month or week.
index 7e0d40d..81094ba 100644 (file)
@@ -120,7 +120,12 @@ class core_useragent {
     protected function __construct($forceuseragent = null) {
         global $CFG;
         if (!empty($CFG->devicedetectregex)) {
-            $this->devicetypecustoms = json_decode($CFG->devicedetectregex);
+            $this->devicetypecustoms = json_decode($CFG->devicedetectregex, true);
+        }
+        if ($this->devicetypecustoms === null) {
+            // This shouldn't happen unless you're hardcoding the config value.
+            debugging('Config devicedetectregex is not valid JSON object');
+            $this->devicetypecustoms = array();
         }
         if ($forceuseragent !== null) {
             $this->useragent = $forceuseragent;
index c882073..c2f99cf 100644 (file)
@@ -212,4 +212,14 @@ $definitions = array(
         'staticaccelerationsize' => 2, // Should not be required for more than one user at a time.
         'ttl' => 3600,
     ),
+
+    // A simple cache that stores whether a user can expand a course in the navigation.
+    // The key is the course ID and the value will either be 1 or 0 (cast to bool).
+    // The cache isn't always up to date, it should only ever be used to save a costly call to
+    // can_access_course on the first page request a user makes.
+    'navigation_expandcourse' => array(
+        'mode' => cache_store::MODE_SESSION,
+        'simplekeys' => true,
+        'simpledata' => true
+    )
 );
index cc81df4..261495a 100644 (file)
@@ -825,6 +825,13 @@ $functions = array(
         'type'        => 'read'
     ),
 
+    'core_grading_save_definitions' => array(
+        'classname'   => 'core_grading_external',
+        'methodname'  => 'save_definitions',
+        'description' => 'Save grading definitions',
+        'type'        => 'write'
+    ),
+
     'core_grading_get_gradingform_instances' => array(
         'classname'   => 'core_grading_external',
         'methodname'  => 'get_gradingform_instances',
index 9ed46e3..bb90953 100644 (file)
@@ -3669,5 +3669,12 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2014060300.00);
     }
 
+    if ($oldversion < 2014061000.00) {
+        // Fixing possible wrong MIME type for Publisher files.
+        $filetypes = array('%.pub'=>'application/x-mspublisher');
+        upgrade_mimetypes($filetypes);
+        upgrade_main_savepoint(true, 2014061000.00);
+    }
+
     return true;
 }
index 7e92a1f..a8dcb6b 100644 (file)
@@ -456,3 +456,23 @@ function upgrade_availability_item($groupmembersonly, $groupingid,
         return null;
     }
 }
+
+/**
+ * Updates the mime-types for files that exist in the database, based on their
+ * file extension.
+ *
+ * @param array $filetypes Array with file extension as the key, and mimetype as the value
+ */
+function upgrade_mimetypes($filetypes) {
+    global $DB;
+    $select = $DB->sql_like('filename', '?', false);
+    foreach ($filetypes as $extension=>$mimetype) {
+        $DB->set_field_select(
+            'files',
+            'mimetype',
+            $mimetype,
+            $select,
+            array($extension)
+        );
+    }
+}
\ No newline at end of file
index 264afcf..499e406 100644 (file)
@@ -2386,6 +2386,7 @@ abstract class moodle_database {
 
         if (empty($this->transactions)) {
             \core\event\manager::database_transaction_commited();
+            \core\message\manager::database_transaction_commited();
         }
     }
 
@@ -2433,6 +2434,7 @@ abstract class moodle_database {
             // finally top most level rolled back
             $this->force_rollback = false;
             \core\event\manager::database_transaction_rolledback();
+            \core\message\manager::database_transaction_rolledback();
         }
         throw $e;
     }
diff --git a/lib/editor/atto/plugins/accessibilitychecker/tests/behat/accessibilitychecker.feature b/lib/editor/atto/plugins/accessibilitychecker/tests/behat/accessibilitychecker.feature
new file mode 100644 (file)
index 0000000..c456bc6
--- /dev/null
@@ -0,0 +1,21 @@
+@editor @editor_atto @atto @atto_accessibilitychecker
+Feature: Atto accessibility checker
+  To write accessible text in Atto, I need to check for accessibility warnings.
+
+  @javascript
+  Scenario: Images with no alt
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Some plain text</p><img src='/broken-image'/><p>Some more text</p>"
+    When I click on "Show more buttons" "button"
+    And I click on "Accessibility checker" "button"
+    Then I should see "Images require alternative text."
+
+  @javascript
+  Scenario: Low contrast
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p style='color: #7c7cff; background-color: #ffffff;'>Hard to read</p>"
+    When I click on "Show more buttons" "button"
+    And I click on "Accessibility checker" "button"
+    Then I should see "The colours of the foreground and background text do not have enough contrast."
diff --git a/lib/editor/atto/plugins/accessibilityhelper/tests/behat/accessibilityhelper.feature b/lib/editor/atto/plugins/accessibilityhelper/tests/behat/accessibilityhelper.feature
new file mode 100644 (file)
index 0000000..a633fd2
--- /dev/null
@@ -0,0 +1,33 @@
+@editor @editor_atto @atto @atto_accessibilityhelper
+Feature: Atto accessibility helper
+  To use a screen reader effectively in Atto, I may need additional information about the text
+
+  @javascript
+  Scenario: Images and links
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Some plain text</p><img src='/broken-image' alt='Image 1'/><p><a href='#fsd'>Some link text</a></p>"
+    And I select the text in the "Description" Atto editor
+    When I click on "Show more buttons" "button"
+    And I click on "Screenreader helper" "button"
+    Then I should see "Links in text editor"
+    And I should see "Some link text"
+    And I should see "Images in text editor"
+    And I should see "Image 1"
+    And I should not see "No images"
+    And I should not see "No links"
+
+  @javascript
+  Scenario: Styles
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Some plain text</p>"
+    When I click on "Show more buttons" "button"
+    And I select the text in the "Description" Atto editor
+    And I click on "Unordered list" "button"
+    And I click on "Screenreader helper" "button"
+    And I select the text in the "Description" Atto editor
+    # This shows the current HTML tags applied to the selected text.
+    # This is required because they are not always read by a screen reader.
+    Then I should see "UL, LI"
+
diff --git a/lib/editor/atto/plugins/align/tests/behat/align.feature b/lib/editor/atto/plugins/align/tests/behat/align.feature
new file mode 100644 (file)
index 0000000..6095ec5
--- /dev/null
@@ -0,0 +1,44 @@
+@editor @editor_atto @atto @atto_align
+Feature: Atto align text
+  To format text in Atto, I need to use the align buttons.
+
+  @javascript
+  Scenario: Right align some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Fascism</p>"
+    And I set the field "Text editor" to "Plain text area"
+    And I click on "Show more buttons" "button"
+    And I select the text in the "Description" Atto editor
+    When I click on "Right align" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "style=\"text-align:right;\""
+
+  @javascript
+  Scenario: Left align some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Communism</p>"
+    And I set the field "Text editor" to "Plain text area"
+    And I click on "Show more buttons" "button"
+    And I select the text in the "Description" Atto editor
+    When I click on "Right align" "button"
+    And I click on "Left align" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "style=\"text-align:left;\""
+
+  @javascript
+  Scenario: Center align some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>United Future</p>"
+    And I set the field "Text editor" to "Plain text area"
+    And I click on "Show more buttons" "button"
+    And I select the text in the "Description" Atto editor
+    When I click on "Center" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "style=\"text-align:center;\""
+
diff --git a/lib/editor/atto/plugins/bold/tests/behat/bold.feature b/lib/editor/atto/plugins/bold/tests/behat/bold.feature
new file mode 100644 (file)
index 0000000..72d7730
--- /dev/null
@@ -0,0 +1,29 @@
+@editor @editor_atto @atto @atto_bold @_bug_phantomjs
+Feature: Atto bold button
+  To format text in Atto, I need to use the bold button.
+
+  @javascript
+  Scenario: Bold some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Description" to "Badger"
+    And I select the text in the "Description" Atto editor
+    When I click on "Bold" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<b>Badger</b>"
+
+  @javascript
+  Scenario: Unbold some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Description" to "Mouse"
+    And I select the text in the "Description" Atto editor
+    When I click on "Bold" "button"
+    And I click on "Bold" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should not see "<b>Mouse</b>"
+    And I should see "Mouse"
diff --git a/lib/editor/atto/plugins/charmap/tests/behat/charmap.feature b/lib/editor/atto/plugins/charmap/tests/behat/charmap.feature
new file mode 100644 (file)
index 0000000..a73ee3f
--- /dev/null
@@ -0,0 +1,18 @@
+@editor @editor_atto @atto @atto_charmap
+Feature: Atto charmap button
+  To format text in Atto, I need to add symbols
+
+  @javascript
+  Scenario: Insert symbols
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Description" to "<p>1980 Mullet</p>"
+    And I select the text in the "Description" Atto editor
+    When I click on "Show more buttons" "button"
+    And I click on "Insert character" "button"
+    And I click on "copyright sign" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "(c)"
+
index 1e33957..a0089aa 100644 (file)
Binary files a/lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button-debug.js and b/lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button-debug.js differ
index 6ae01e9..33926cd 100644 (file)
Binary files a/lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button-min.js and b/lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button-min.js differ
index 1e33957..a0089aa 100644 (file)
Binary files a/lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button.js and b/lib/editor/atto/plugins/charmap/yui/build/moodle-atto_charmap-button/moodle-atto_charmap-button.js differ
index f777e3b..571ad63 100644 (file)
@@ -408,5 +408,8 @@ Y.namespace('M.atto_charmap').Button = Y.Base.create('button', Y.M.editor_atto.E
 
         // And add the character.
         host.insertContentAtFocusPoint(character);
+
+        // And mark the text area as updated.
+        this.markUpdated();
     }
 });
diff --git a/lib/editor/atto/plugins/clear/tests/behat/clear.feature b/lib/editor/atto/plugins/clear/tests/behat/clear.feature
new file mode 100644 (file)
index 0000000..b701da3
--- /dev/null
@@ -0,0 +1,19 @@
+@editor @editor_atto @atto @atto_clear @_bug_phantomjs
+Feature: Atto clear button
+  To format text in Atto, I need to remove formatting
+
+  @javascript
+  Scenario: Clear formatting
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Description" to "Pisa"
+    And I select the text in the "Description" Atto editor
+    And I click on "Italic" "button"
+    And I click on "Show more buttons" "button"
+    And I select the text in the "Description" Atto editor
+    When I click on "Clear formatting" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should not see "<i>Pisa"
+
diff --git a/lib/editor/atto/plugins/collapse/tests/behat/collapse.feature b/lib/editor/atto/plugins/collapse/tests/behat/collapse.feature
new file mode 100644 (file)
index 0000000..ab37d18
--- /dev/null
@@ -0,0 +1,13 @@
+@editor @editor_atto @atto @atto_collapse
+Feature: Atto collapse button
+  To access all the tools in Atto, I need to toggle the toolbar
+
+  @javascript
+  Scenario: Toggle toolbar
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    When I click on "Show more buttons" "button"
+    Then "Equation editor" "button" should be visible
+    And I click on "Show fewer buttons" "button"
+    Then "Equation editor" "button" should not be visible
+
diff --git a/lib/editor/atto/plugins/equation/tests/behat/equation.feature b/lib/editor/atto/plugins/equation/tests/behat/equation.feature
new file mode 100644 (file)
index 0000000..0b20f46
--- /dev/null
@@ -0,0 +1,21 @@
+@editor @editor_atto @atto @atto_equation
+Feature: Atto equation editor
+  To teach maths to students, I need to write equations
+
+  @javascript
+  Scenario: Create an equation
+    Given I log in as "admin"
+    When I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Equation test</p>"
+    # Set field on the bottom of page, so equation editor dialogue is visible.
+    And I expand all fieldsets
+    And I set the field "Picture description" to "Test"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    And I click on "Equation editor" "button"
+    And I set the field "Edit equation using" to " = 1 \div 0"
+    And I click on "\infty" "button"
+    And I click on "Save equation" "button"
+    And I click on "Update profile" "button"
+    Then "\infty" "text" should exist
+
diff --git a/lib/editor/atto/plugins/html/tests/behat/html.feature b/lib/editor/atto/plugins/html/tests/behat/html.feature
new file mode 100644 (file)
index 0000000..35f02fc
--- /dev/null
@@ -0,0 +1,13 @@
+@editor @editor_atto @atto @atto_html
+Feature: Atto edit HTML
+  To write advanced HTML, I need to edit the HTML source code
+
+  @javascript
+  Scenario: Edit the html source
+    Given I log in as "admin"
+    When I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p style=\"color: blue;\">Smurf</p>"
+    And I click on "Show more buttons" "button"
+    And I click on "HTML" "button"
+    Then the field "Description" matches value "<p style=\"color: blue;\">Smurf</p>"
+
diff --git a/lib/editor/atto/plugins/image/tests/behat/image.feature b/lib/editor/atto/plugins/image/tests/behat/image.feature
new file mode 100644 (file)
index 0000000..3620a89
--- /dev/null
@@ -0,0 +1,28 @@
+@editor @editor_atto @atto @atto_image @_file_upload
+Feature: Add images to Atto
+  To write rich text - I need to add images.
+
+  @javascript
+  Scenario: Insert an image
+    Given I log in as "admin"
+    And I navigate to "My private files" node in "My profile"
+    And I upload "lib/editor/atto/tests/fixtures/moodle-logo.png" file to "Files" filemanager
+    And I click on "Save changes" "button"
+    When I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>Image test</p>"
+    And I select the text in the "Description" Atto editor
+    And I click on "Image" "button"
+    And I click on "Browse repositories..." "button"
+    And I click on "Private files" "link"
+    And I click on "moodle-logo.png" "link"
+    And I click on "Select this file" "button"
+    And I set the field "Describe this image" to "It's the Moodle"
+    # Wait for the page to "settle".
+    And I wait "2" seconds
+    And I click on "Save image" "button"
+    And I click on "Update profile" "button"
+    And I follow "Edit profile"
+    And I select the text in the "Description" Atto editor
+    And I click on "Image" "button"
+    Then the field "Describe this image" matches value "It's the Moodle"
+
diff --git a/lib/editor/atto/plugins/indent/tests/behat/indent.feature b/lib/editor/atto/plugins/indent/tests/behat/indent.feature
new file mode 100644 (file)
index 0000000..212d7c0
--- /dev/null
@@ -0,0 +1,30 @@
+@editor @editor_atto @atto @atto_indent
+Feature: Indent text in Atto
+  To write rich text - I need to indent and outdent things.
+
+  @javascript
+  Scenario: Indent
+    Given I log in as "admin"
+    When I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>I need some space.</p>"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    And I click on "Indent" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "class=\"editor-indent\""
+
+  @javascript
+  Scenario: Indent and outdent
+    Given I log in as "admin"
+    When I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "<p>I need some space.</p>"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    And I click on "Indent" "button"
+    And I click on "Outdent" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should not see "class=\"editor-indent\""
diff --git a/lib/editor/atto/plugins/italic/tests/behat/italic.feature b/lib/editor/atto/plugins/italic/tests/behat/italic.feature
new file mode 100644 (file)
index 0000000..3efcea7
--- /dev/null
@@ -0,0 +1,29 @@
+@editor @editor_atto @atto @atto_italic @_bug_phantomjs
+Feature: Atto italic button
+  To format text in Atto, I need to use the italic button.
+
+  @javascript
+  Scenario: Italicise some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "Tower of Pisa"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    When I click on "Italic" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<i>Tower of Pisa</i>"
+
+  @javascript
+  Scenario: Toggle italics in some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "GHD - for hair"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    When I click on "Italic" "button"
+    And I click on "Italic" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should not see "<i>GHD - for hair</i>"
+    And I should see "GHD - for hair"
diff --git a/lib/editor/atto/plugins/link/tests/behat/link.feature b/lib/editor/atto/plugins/link/tests/behat/link.feature
new file mode 100644 (file)
index 0000000..444213b
--- /dev/null
@@ -0,0 +1,23 @@
+@editor @editor_atto @atto @atto_link @_file_upload
+Feature: Add links to Atto
+  To write rich text - I need to add links.
+
+  @javascript
+  Scenario: Insert a links
+    Given I log in as "admin"
+    And I navigate to "My private files" node in "My profile"
+    And I upload "lib/editor/atto/tests/fixtures/moodle-logo.png" file to "Files" filemanager
+    And I click on "Save changes" "button"
+    When I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Description" to "Super cool"
+    And I select the text in the "Description" Atto editor
+    And I click on "Link" "button"
+    And I click on "Browse repositories..." "button"
+    And I click on "Private files" "link"
+    And I click on "moodle-logo.png" "link"
+    And I click on "Select this file" "button"
+    And I click on "Update profile" "button"
+    And I follow "Edit profile"
+    Then I should see "Super cool</a>"
+
index 851d740..72a694a 100644 (file)
Binary files a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js and b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js differ
index 8b51ba9..bcdefd1 100644 (file)
Binary files a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js and b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js differ
index 851d740..72a694a 100644 (file)
Binary files a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js and b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js differ
index 5f66ca2..c78379e 100644 (file)
@@ -181,6 +181,8 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
             this.get('host').setSelection(this._currentSelection);
             document.execCommand('unlink', false, null);
             document.execCommand('createLink', false, params.url);
+            // And mark the text area as updated.
+            this.markUpdated();
         }
     },
 
diff --git a/lib/editor/atto/plugins/media/tests/behat/media.feature b/lib/editor/atto/plugins/media/tests/behat/media.feature
new file mode 100644 (file)
index 0000000..fb5232a
--- /dev/null
@@ -0,0 +1,27 @@
+@editor @editor_atto @atto @atto_media @_file_upload
+Feature: Add media to Atto
+  To write rich text - I need to add media.
+
+  @javascript
+  Scenario: Insert some media
+    Given I log in as "admin"
+    And I navigate to "My private files" node in "My profile"
+    And I upload "lib/editor/atto/tests/fixtures/moodle-logo.webm" file to "Files" filemanager
+    And I click on "Save changes" "button"
+    When I am on homepage
+    And I expand "My profile" node
+    And I expand "Blogs" node
+    And I follow "Add a new entry"
+    And I set the field "Blog entry body" to "<p>Media test</p>"
+    And I select the text in the "Blog entry body" Atto editor
+    And I set the field "Entry title" to "The best video in the entire world (not really)"
+    And I click on "Media" "button"
+    And I click on "Browse repositories..." "button"
+    And I click on "Private files" "link"
+    And I click on "moodle-logo.webm" "link"
+    And I click on "Select this file" "button"
+    And I set the field "Enter name" to "It's the logo"
+    And I click on "Insert media" "button"
+    And I click on "Save changes" "button"
+    Then "video" "css_element" should be visible
+
diff --git a/lib/editor/atto/plugins/orderedlist/tests/behat/orderedlist.feature b/lib/editor/atto/plugins/orderedlist/tests/behat/orderedlist.feature
new file mode 100644 (file)
index 0000000..5e77c79
--- /dev/null
@@ -0,0 +1,16 @@
+@editor @editor_atto @atto @atto_orderedlist @_bug_phantomjs
+Feature: Atto ordered list button
+  To format text in Atto, I need to use the ordered list button.
+
+  @javascript
+  Scenario: Make a list from some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "Have you heard the news everyone's talking<br/> Life is good 'cause everything's awesome<br/>"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    When I click on "Ordered list" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<ol><li>Have you heard the news everyone's talking"
+
diff --git a/lib/editor/atto/plugins/strike/tests/behat/strike.feature b/lib/editor/atto/plugins/strike/tests/behat/strike.feature
new file mode 100644 (file)
index 0000000..f919947
--- /dev/null
@@ -0,0 +1,17 @@
+@editor @editor_atto @atto @atto_strike @_bug_phantomjs
+Feature: Atto strike button
+  To format text in Atto, I need to use the strike button.
+
+  @javascript
+  Scenario: Strike some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "MUA"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    When I click on "Strike through" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<strike>MUA</strike>"
+
diff --git a/lib/editor/atto/plugins/subscript/tests/behat/subscript.feature b/lib/editor/atto/plugins/subscript/tests/behat/subscript.feature
new file mode 100644 (file)
index 0000000..bc59778
--- /dev/null
@@ -0,0 +1,17 @@
+@editor @editor_atto @atto @atto_subscript @_bug_phantomjs
+Feature: Atto subscript button
+  To format text in Atto, I need to use the subscript button.
+
+  @javascript
+  Scenario: Subscript some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "Submarine"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    When I click on "Subscript" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<sub>Submarine</sub>"
+
diff --git a/lib/editor/atto/plugins/superscript/tests/behat/superscript.feature b/lib/editor/atto/plugins/superscript/tests/behat/superscript.feature
new file mode 100644 (file)
index 0000000..703fd96
--- /dev/null
@@ -0,0 +1,17 @@
+@editor @editor_atto @atto @atto_superscript @_bug_phantomjs
+Feature: Atto superscript button
+  To format text in Atto, I need to use the superscript button.
+
+  @javascript
+  Scenario: Subscript some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "Helicopter"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    When I click on "Superscript" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<sup>Helicopter</sup>"
+
diff --git a/lib/editor/atto/plugins/table/tests/behat/table.feature b/lib/editor/atto/plugins/table/tests/behat/table.feature
new file mode 100644 (file)
index 0000000..2915445
--- /dev/null
@@ -0,0 +1,40 @@
+@editor @editor_atto @atto @atto_table @_bug_phantomjs
+Feature: Atto tables
+  To format text in Atto, I need to create tables
+
+  @javascript
+  Scenario: Create a table
+    Given I log in as "admin"
+    And I am on homepage
+    And I expand "My profile" node
+    And I expand "Blogs" node
+    And I follow "Add a new entry"
+    And I set the field "Entry title" to "How to make a table"
+    And I set the field "Blog entry body" to "Table test"
+    And I select the text in the "Blog entry body" Atto editor
+    And I click on "Show more buttons" "button"
+    When I click on "Table" "button"
+    And I set the field "Caption" to "Dinner"
+    And I press "Create table"
+    And I press "Save changes"
+    Then ".blog_entry table caption" "css_element" should be visible
+
+  @javascript
+  Scenario: Edit a table
+    Given I log in as "admin"
+    And I am on homepage
+    And I expand "My profile" node
+    And I expand "Blogs" node
+    And I follow "Add a new entry"
+    And I set the field "Entry title" to "How to make a table"
+    And I set the field "Blog entry body" to "<table><tr><td>Cell</td></tr></table>"
+    And I select the text in the "Blog entry body" Atto editor
+    And I click on "Show more buttons" "button"
+    And I click on "Table" "button"
+    And I click on "Edit table" "link"
+    And I set the field "Caption" to "Dinner"
+    And I press "Update table"
+    And I press "Save changes"
+    Then ".blog_entry table caption" "css_element" should be visible
+
+
index 221375f..cd32dee 100644 (file)
Binary files a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js and b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js differ
index d175700..42b5578 100644 (file)
Binary files a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js and b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js differ
index 221375f..cd32dee 100644 (file)
Binary files a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js and b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js differ
index a34ef41..6c1389a 100644 (file)
@@ -389,6 +389,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
 
             }, this);
         }
+        // Clean the HTML.
+        this.markUpdated();
     },
 
     /**
diff --git a/lib/editor/atto/plugins/title/tests/behat/title.feature b/lib/editor/atto/plugins/title/tests/behat/title.feature
new file mode 100644 (file)
index 0000000..15e4e8e
--- /dev/null
@@ -0,0 +1,17 @@
+@editor @editor_atto @atto @atto_title
+Feature: Atto title
+  To format text in Atto, I need to add headings
+
+  @javascript
+  Scenario: Create a heading
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Description" to "How The Rock Has Made the WWE World Heavyweight Title More Important Than Ever"
+    And I select the text in the "Description" Atto editor
+    When I click on "Paragraph styles" "button"
+    When I click on "Heading 1" "link"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<h3>How The Rock"
+
diff --git a/lib/editor/atto/plugins/underline/tests/behat/underline.feature b/lib/editor/atto/plugins/underline/tests/behat/underline.feature
new file mode 100644 (file)
index 0000000..c2d532b
--- /dev/null
@@ -0,0 +1,17 @@
+@editor @editor_atto @atto @atto_underline @_bug_phantomjs
+Feature: Atto underline button
+  To format text in Atto, I need to use the underline button.
+
+  @javascript
+  Scenario: Underline some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "Deprecated HTML Tag"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    And I click on "Show more buttons" "button"
+    When I click on "Underline" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<u>Deprecated HTML Tag</u>"
+
diff --git a/lib/editor/atto/plugins/unorderedlist/tests/behat/unorderedlist.feature b/lib/editor/atto/plugins/unorderedlist/tests/behat/unorderedlist.feature
new file mode 100644 (file)
index 0000000..30a8b90
--- /dev/null
@@ -0,0 +1,16 @@
+@editor @editor_atto @atto @atto_unorderedlist @_bug_phantomjs
+Feature: Atto unordered list button
+  To format text in Atto, I need to use the unordered list button.
+
+  @javascript
+  Scenario: Make a list from some text
+    Given I log in as "admin"
+    And I navigate to "Edit profile" node in "My profile settings"
+    And I set the field "Description" to "Things, dogs, clogs, they're awesome<br/> Rocks, clocks, and socks, they're awesome<br/> Figs, and wigs, and twigs, that's awesome<br/> Everything you see or think or say is awesome"
+    And I set the field "Text editor" to "Plain text area"
+    And I select the text in the "Description" Atto editor
+    When I click on "Unordered list" "button"
+    And I press "Update profile"
+    And I follow "Edit profile"
+    Then I should see "<ul><li>Things, dogs, clogs"
+
diff --git a/lib/editor/atto/tests/behat/behat_editor_atto.php b/lib/editor/atto/tests/behat/behat_editor_atto.php
new file mode 100644 (file)
index 0000000..91723d8
--- /dev/null
@@ -0,0 +1,62 @@
+<?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/>.
+
+/**
+ * Atto custom steps definitions.
+ *
+ * @package    editor_atto
+ * @category   test
+ * @copyright  2014 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+/**
+ * Steps definitions to deal with the atto text editor
+ *
+ * @package    editor_atto
+ * @category   test
+ * @copyright  2014 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_editor_atto extends behat_base {
+
+    /**
+     * Select the text in an Atto field.
+     *
+     * @Given /^I select the text in the "([^"]*)" Atto editor$/
+     * @throws ElementNotFoundException Thrown by behat_base::find
+     * @param string $field
+     * @return void
+     */
+    public function select_the_text_in_the_atto_editor($fieldlocator) {
+        if (!$this->running_javascript()) {
+            throw new coding_exception('Selecting text requires javascript.');
+        }
+        // We delegate to behat_form_field class, it will
+        // guess the type properly.
+        $field = behat_field_manager::get_form_field_from_label($fieldlocator, $this);
+
+        if (!method_exists($field, 'select_text')) {
+            throw new coding_exception('Field does not support the select_text function.');
+        }
+        $field->select_text();
+    }
+
+
+}
+
diff --git a/lib/editor/atto/tests/fixtures/moodle-logo.png b/lib/editor/atto/tests/fixtures/moodle-logo.png
new file mode 100644 (file)
index 0000000..efed023
Binary files /dev/null and b/lib/editor/atto/tests/fixtures/moodle-logo.png differ
diff --git a/lib/editor/atto/tests/fixtures/moodle-logo.webm b/lib/editor/atto/tests/fixtures/moodle-logo.webm
new file mode 100644 (file)
index 0000000..20a97be
Binary files /dev/null and b/lib/editor/atto/tests/fixtures/moodle-logo.webm differ
index 3adfbd4..cde46db 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js and b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js differ
index 01dfab6..ecebe59 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js and b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js differ
index 963a932..3654b27 100644 (file)
Binary files a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js and b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js differ
index 6804837..8aad568 100644 (file)
@@ -206,7 +206,7 @@ EditorPluginButtons.prototype = {
         var title = M.util.get_string(config.title, 'atto_' + pluginname);
 
         // Create the actual button.
-        button = Y.Node.create('<button class="' + buttonClass + '"' +
+        button = Y.Node.create('<button type="button" class="' + buttonClass + '"' +
                 'tabindex="-1">' +
                     '<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" src="' + config.iconurl + '"/>' +
                 '</button>');
index 9297dad..2724d5c 100644 (file)
@@ -9,8 +9,7 @@ Feature: Add or remove items from the TinyMCE editor toolbar
       | fullname | shortname | category |
       | Course 1 | C1 | 0 |
     And I log in as "admin"
-    And I follow "Admin User"
-    And I follow "Edit profile"
+    And I navigate to "Edit profile" node in "My profile settings"
     And I set the field "Text editor" to "TinyMCE HTML editor"
     And I press "Update profile"
     And I follow "Home"
index c8d6422..624558b 100644 (file)
@@ -48,7 +48,7 @@ class MoodleExcelWorkbook {
      * Constructs one Moodle Workbook.
      *
      * @param string $filename The name of the file
-     * @param string $type file format type 'Excel5' or 'Excel2007'
+     * @param string $type file format type used to be 'Excel5 or Excel2007' but now only 'Excel2007'
      */
     public function __construct($filename, $type = 'Excel2007') {
         global $CFG;
@@ -60,7 +60,8 @@ class MoodleExcelWorkbook {
         $this->filename = $filename;
 
         if (strtolower($type) === 'excel5') {
-            $this->type = 'Excel5';
+            debugging('Excel5 is no longer supported, using Excel2007 instead');
+            $this->type = 'Excel2007';
         } else {
             $this->type = 'Excel2007';
         }
@@ -102,13 +103,8 @@ class MoodleExcelWorkbook {
 
         $filename = preg_replace('/\.xlsx?$/i', '', $this->filename);
 
-        if ($this->type === 'Excel5') {
-            $mimetype = 'application/vnd.ms-excel';
-            $filename = $filename.'.xls';
-        } else {
-            $mimetype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
-            $filename = $filename.'.xlsx';
-        }
+        $mimetype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+        $filename = $filename.'.xlsx';
 
         if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
             header('Cache-Control: max-age=10');
index 6e1b9f6..f6a3316 100644 (file)
@@ -1507,7 +1507,6 @@ function &get_mimetypes_array() {
         'pic'  => array ('type'=>'image/pict', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
         'pict' => array ('type'=>'image/pict', 'icon'=>'image', 'groups'=>array('image'), 'string'=>'image'),
         'png'  => array ('type'=>'image/png', 'icon'=>'png', 'groups'=>array('image', 'web_image'), 'string'=>'image'),
-
         'pps'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint', 'groups'=>array('presentation')),
         'ppt'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint', 'groups'=>array('presentation')),
         'pptx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'icon'=>'powerpoint'),
@@ -1517,8 +1516,9 @@ function &get_mimetypes_array() {
         'ppam' => array ('type'=>'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'icon'=>'powerpoint'),
         'ppsx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'icon'=>'powerpoint'),
         'ppsm' => array ('type'=>'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'icon'=>'powerpoint'),
-
         'ps'   => array ('type'=>'application/postscript', 'icon'=>'pdf'),
+        'pub'  => array ('type'=>'application/x-mspublisher', 'icon'=>'publisher', 'groups'=>array('presentation')),
+
         'qt'   => array ('type'=>'video/quicktime', 'icon'=>'quicktime', 'groups'=>array('video','web_video'), 'string'=>'video'),
         'ra'   => array ('type'=>'audio/x-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio','web_audio'), 'string'=>'audio'),
         'ram'  => array ('type'=>'audio/x-pn-realaudio-plugin', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
index 6b4645d..ed15afc 100644 (file)
@@ -205,7 +205,14 @@ class stored_file {
      */
     public function rename($filepath, $filename) {
         if ($this->fs->file_exists($this->get_contextid(), $this->get_component(), $this->get_filearea(), $this->get_itemid(), $filepath, $filename)) {
-            throw new file_exception('storedfilenotcreated', '', 'file exists, cannot rename');
+            $a = new stdClass();
+            $a->contextid = $this->get_contextid();
+            $a->component = $this->get_component();
+            $a->filearea  = $this->get_filearea();
+            $a->itemid    = $this->get_itemid();
+            $a->filepath  = $filepath;
+            $a->filename  = $filename;
+            throw new file_exception('storedfilenotcreated', $a, 'file exists, cannot rename');
         }
         $filerecord = new stdClass;
         $filerecord->filepath = $filepath;
index abd8c1d..9c7ff1d 100644 (file)
@@ -300,7 +300,8 @@ class core_files_file_storage_testcase extends advanced_testcase {
         $this->assertEquals($contenthash, $file->get_contenthash());
 
         // Try break it.
-        $this->setExpectedException('file_exception');
+        $this->setExpectedException('file_exception',
+                'Can not create file "1/core/unittest/0/test/newtest.txt" (file exists, cannot rename)');
         // This shall throw exception.
         $originalfile->rename($newpath, $newname);
     }
@@ -1146,7 +1147,7 @@ class core_files_file_storage_testcase extends advanced_testcase {
         $this->assertInstanceOf('stored_file', $file1);
 
         // Creating a file validating unique constraint.
-        $this->setExpectedException('stored_file_creation_exception');
+        $this->setExpectedException('stored_file_creation_exception', 'Can not create file "1/core/phpunit/0/testfile.txt"');
         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
     }
 
@@ -1426,7 +1427,7 @@ class core_files_file_storage_testcase extends advanced_testcase {
         $this->assertInstanceOf('stored_file', $file1);
 
         // Creating a file validating unique constraint.
-        $this->setExpectedException('stored_file_creation_exception');
+        $this->setExpectedException('stored_file_creation_exception', 'Can not create file "1/core/phpunit/0/testfile.txt"');
         $file2 = $fs->create_file_from_pathname($filerecord, $path);
     }
 
index 02c6cfc..8f9ee6a 100644 (file)
@@ -47,9 +47,12 @@ require_once(dirname(dirname(__FILE__)) . '/message/lib.php');
  *  contexturl string if this is a notification then you can specify a url to view the event. For example the forum post the user is being notified of.
  *  contexturlname string the display text for contexturl
  *
+ * Note: processor failure is is not reported as false return value,
+ *       earlier versions did not do it consistently either.
+ *
  * @category message
- * @param object $eventdata information about the message (component, userfrom, userto, ...)
- * @return mixed the integer ID of the new message or false if there was a problem with a processor
+ * @param stdClass $eventdata information about the message (component, userfrom, userto, ...)
+ * @return mixed the integer ID of the new message or false if there was a problem with submitted data
  */
 function message_send($eventdata) {
     global $CFG, $DB;
@@ -65,20 +68,33 @@ function message_send($eventdata) {
         return $messageid;
     }
 
-    //TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
-    $DB->transactions_forbidden();
-
     // By default a message is a notification. Only personal/private messages aren't notifications.
     if (!isset($eventdata->notification)) {
         $eventdata->notification = 1;
     }
 
-    if (is_number($eventdata->userto)) {
+    if (!is_object($eventdata->userto)) {
         $eventdata->userto = core_user::get_user($eventdata->userto);
     }
-    if (is_int($eventdata->userfrom)) {
+    if (!is_object($eventdata->userfrom)) {
         $eventdata->userfrom = core_user::get_user($eventdata->userfrom);
     }
+    if (!$eventdata->userto) {
+        debugging('Attempt to send msg to unknown user', DEBUG_NORMAL);
+        return false;
+    }
+    if (!$eventdata->userfrom) {
+        debugging('Attempt to send msg from unknown user', DEBUG_NORMAL);
+        return false;
+    }
+
+    // Verify all necessary data fields are present.
+    if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended)
+            or !isset($eventdata->userto->deleted) or !isset($eventdata->userto->emailstop)) {
+
+        debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER);
+        $eventdata->userto = core_user::get_user($eventdata->userto->id);
+    }
 
     $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false);
     // If recipient is internal user (noreply user), and emailstop is set then don't send any msg.
@@ -87,10 +103,6 @@ function message_send($eventdata) {
         return false;
     }
 
-    if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended) or !isset($eventdata->userto->deleted)) {
-        $eventdata->userto = core_user::get_user($eventdata->userto->id);
-    }
-
     //after how long inactive should the user be considered logged off?
     if (isset($CFG->block_online_users_timetosee)) {
         $timetoshowusers = $CFG->block_online_users_timetosee * 60;
@@ -117,13 +129,13 @@ function message_send($eventdata) {
     $savemessage->notification      = $eventdata->notification;
 
     if (!empty($eventdata->contexturl)) {
-        $savemessage->contexturl = $eventdata->contexturl;
+        $savemessage->contexturl = (string)$eventdata->contexturl;
     } else {
         $savemessage->contexturl = null;
     }
 
     if (!empty($eventdata->contexturlname)) {
-        $savemessage->contexturlname = $eventdata->contexturlname;
+        $savemessage->contexturlname = (string)$eventdata->contexturlname;
     } else {
         $savemessage->contexturlname = null;
     }
@@ -189,12 +201,6 @@ function message_send($eventdata) {
             debugging('Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL);
         }
 
-        // Warn developers that necessary data is missing regardless of how the processors are configured
-        if (!isset($eventdata->userto->emailstop)) {
-            debugging('userto->emailstop is not set. Retrieving it from the user table');
-            $eventdata->userto->emailstop = $DB->get_field('user', 'emailstop', array('id'=>$eventdata->userto->id));
-        }
-
         // Populate the list of processors we will be using
         if ($permitted == 'forced' && $userisconfigured) {
             // An admin is forcing users to use this message processor. Use this processor unconditionally.
@@ -203,7 +209,7 @@ function message_send($eventdata) {
             // User has not disabled notifications
             // See if user set any notification preferences, otherwise use site default ones
             $userpreferencename = 'message_provider_'.$preferencebase.'_'.$userstate;
-            if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto->id)) {
+            if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto)) {
                 if (in_array($processor->name, explode(',', $userpreference))) {
                     $processorlist[] = $processor->name;
                 }
@@ -215,60 +221,12 @@ function message_send($eventdata) {
         }
     }
 
-    if (empty($processorlist) && $savemessage->notification) {
-        //if they have deselected all processors and its a notification mark it read. The user doesnt want to be bothered
-        $savemessage->timeread = time();
-        $messageid = $DB->insert_record('message_read', $savemessage);
-    } else {                        // Process the message
-        // Store unread message just in case we can not send it
-        $messageid = $savemessage->id = $DB->insert_record('message', $savemessage);
-        $eventdata->savedmessageid = $savemessage->id;
-
-        // Try to deliver the message to each processor
-        if (!empty($processorlist)) {
-            foreach ($processorlist as $procname) {
-                if (!$processors[$procname]->object->send_message($eventdata)) {
-                    debugging('Error calling message processor '.$procname);
-                    $messageid = false;
-                }
-            }
-
-            //if messaging is disabled and they previously had forum notifications handled by the popup processor
-            //or any processor that puts a row in message_working then the notification will remain forever
-            //unread. To prevent this mark the message read if messaging is disabled
-            if (empty($CFG->messaging)) {
-                require_once($CFG->dirroot.'/message/lib.php');
-                $messageid = message_mark_message_read($savemessage, time());
-            } else if ( $DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0){
-                //if there is no more processors that want to process this we can move message to message_read
-                require_once($CFG->dirroot.'/message/lib.php');
-                $messageid = message_mark_message_read($savemessage, time(), true);
-            }
-        }
-    }
-
-    // We may be sending a message from the 'noreply' address, which means we are not actually sending a
-    // message from a valid user. In this case, we will set the userid to 0.
-    // Check if the userid is valid.
-    if (core_user::is_real_user($eventdata->userfrom->id)) {
-        $userfromid = $eventdata->userfrom->id;
-    } else {
-        $userfromid = 0;
-    }
+    // Store unread message just in case we get a fatal error any time later.
+    $savemessage->id = $DB->insert_record('message', $savemessage);
+    $eventdata->savedmessageid = $savemessage->id;
 
-    // Trigger event for sending a message.
-    $event = \core\event\message_sent::create(array(
-        'userid' => $userfromid,
-        'context'  => context_system::instance(),
-        'relateduserid' => $eventdata->userto->id,
-        'other' => array(
-            'messageid' => $messageid // Can't use this as the objectid as it can either be the id in the 'message_read'
-                                      // or 'message' table.
-        )
-    ));
-    $event->trigger();
-
-    return $messageid;
+    // Let the manager do the sending or buffering when db transaction in progress.
+    return \core\message\manager::send_message($eventdata, $savemessage, $processorlist);
 }
 
 
index 3b76dd9..cc480f4 100644 (file)
@@ -5166,7 +5166,6 @@ function remove_course_contents($courseid, $showfeedback = true, array $options
     // This array stores the tables that need to be cleared, as
     // table_name => column_name that contains the course id.
     $tablestoclear = array(
-        'log' => 'course',               // Course logs (NOTE: this might be changed in the future).
         'backup_courses' => 'courseid',  // Scheduled backup stuff.
         'user_lastaccess' => 'courseid', // User access info.
     );
@@ -5315,11 +5314,6 @@ function reset_course_userdata($data) {
         $status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => false);
     }
 
-    if (!empty($data->reset_logs)) {
-        $DB->delete_records('log', array('course' => $data->courseid));
-        $status[] = array('component' => $componentstr, 'item' => get_string('deletelogs'), 'error' => false);
-    }
-
     if (!empty($data->reset_events)) {
         $DB->delete_records('event', array('courseid' => $data->courseid));
         $status[] = array('component' => $componentstr, 'item' => get_string('deleteevents', 'calendar'), 'error' => false);
index eac7afe..0ea480b 100644 (file)
@@ -979,7 +979,7 @@ class global_navigation extends navigation_node {
     protected $initialised = false;
     /** @var array An array of course information */
     protected $mycourses = array();
-    /** @var array An array for containing  root navigation nodes */
+    /** @var navigation_node[] An array for containing  root navigation nodes */
     protected $rootnodes = array();
     /** @var bool A switch for whether to show empty sections in the navigation */
     protected $showemptysections = true;
@@ -1001,6 +1001,8 @@ class global_navigation extends navigation_node {
     protected $expansionlimit = 0;
     /** @var int userid to allow parent to see child's profile page navigation */
     protected $useridtouseforparentchecks = 0;
+    /** @var cache_session A cache that stores information on expanded courses */
+    protected $cacheexpandcourse = null;
 
     /** Used when loading categories to load all top level categories [parent = 0] **/
     const LOAD_ROOT_CATEGORIES = 0;
@@ -1131,8 +1133,13 @@ class global_navigation extends navigation_node {
             $this->rootnodes['courses']->isexpandable = true;
         }
 
-        if ($this->rootnodes['mycourses']->isactive) {
+        // Load the users enrolled courses if they are viewing the My Moodle page AND the admin has not
+        // set that they wish to keep the My Courses branch collapsed by default.
+        if (!empty($CFG->navexpandmycourses) && $this->rootnodes['mycourses']->isactive){
             $this->load_courses_enrolled();
+        } else {
+            $this->rootnodes['mycourses']->collapse = true;
+            $this->rootnodes['mycourses']->make_inactive();
         }
 
         $canviewcourseprofile = true;
@@ -1166,6 +1173,14 @@ class global_navigation extends navigation_node {
 
                 // Not enrolled, can't view, and hasn't switched roles
                 if (!can_access_course($course)) {
+                    if ($coursenode->isexpandable === true) {
+                        // Obviously the situation has changed, update the cache and adjust the node.
+                        // This occurs if the user access to a course has been revoked (one way or another) after
+                        // initially logging in for this session.
+                        $this->get_expand_course_cache()->set($course->id, 1);
+                        $coursenode->isexpandable = true;
+                        $coursenode->nodetype = self::NODETYPE_BRANCH;
+                    }
                     // Very ugly hack - do not force "parents" to enrol into course their child is enrolled in,
                     // this hack has been propagated from user/view.php to display the navigation node. (MDL-25805)
                     if (!$this->current_user_is_parent_role()) {
@@ -1175,6 +1190,15 @@ class global_navigation extends navigation_node {
                     }
                 }
 
+                if ($coursenode->isexpandable === false) {
+                    // Obviously the situation has changed, update the cache and adjust the node.
+                    // This occurs if the user has been granted access to a course (one way or another) after initially
+                    // logging in for this session.
+                    $this->get_expand_course_cache()->set($course->id, 1);
+                    $coursenode->isexpandable = true;
+                    $coursenode->nodetype = self::NODETYPE_BRANCH;
+                }
+
                 // Add the essentials such as reports etc...
                 $this->add_course_essentials($coursenode, $course);
                 // Extend course navigation with it's sections/activities
@@ -2373,6 +2397,8 @@ class global_navigation extends navigation_node {
         // This is the name that will be shown for the course.
         $coursename = empty($CFG->navshowfullcoursenames) ? $shortname : $fullname;
 
+        // Can the user expand the course to see its content.
+        $canexpandcourse = true;
         if ($issite) {
             $parent = $this;
             $url = null;
@@ -2392,6 +2418,8 @@ class global_navigation extends navigation_node {
         } else {
             $parent = $this->rootnodes['courses'];
             $url = new moodle_url('/course/view.php', array('id'=>$course->id));
+            // They can only expand the course if they can access it.
+            $canexpandcourse = $this->can_expand_course($course);
             if (!empty($course->category) && $this->show_categories($coursetype == self::COURSE_MY)) {
                 if (!$this->is_category_fully_loaded($course->category)) {
                     // We need to load the category structure for this course
@@ -2408,11 +2436,18 @@ class global_navigation extends navigation_node {
         }
 
         $coursenode = $parent->add($coursename, $url, self::TYPE_COURSE, $shortname, $course->id);
-        $coursenode->nodetype = self::NODETYPE_BRANCH;
         $coursenode->hidden = (!$course->visible);
         // We need to decode &amp;'s here as they will have been added by format_string above and attributes will be encoded again
         // later.
         $coursenode->title(str_replace('&amp;', '&', $fullname));
+        if ($canexpandcourse) {
+            // This course can be expanded by the user, make it a branch to make the system aware that its expandable by ajax.
+            $coursenode->nodetype = self::NODETYPE_BRANCH;
+            $coursenode->isexpandable = true;
+        } else {
+            $coursenode->nodetype = self::NODETYPE_LEAF;
+            $coursenode->isexpandable = false;
+        }
         if (!$forcegeneric) {
             $this->addedcourses[$course->id] = $coursenode;
         }
@@ -2420,6 +2455,45 @@ class global_navigation extends navigation_node {
         return $coursenode;
     }
 
+    /**
+     * Returns a cache instance to use for the expand course cache.
+     * @return cache_session
+     */
+    protected function get_expand_course_cache() {
+        if ($this->cacheexpandcourse === null) {
+            $this->cacheexpandcourse = cache::make('core', 'navigation_expandcourse');
+        }
+        return $this->cacheexpandcourse;
+    }
+
+    /**
+     * Checks if a user can expand a course in the navigation.
+     *
+     * We use a cache here because in order to be accurate we need to call can_access_course which is a costly function.
+     * Because this functionality is basic + non-essential and because we lack good event triggering this cache
+     * permits stale data.
+     * In the situation the user is granted access to a course after we've initialised this session cache the cache
+     * will be stale.
+     * It is brought up to date in only one of two ways.
+     *   1. The user logs out and in again.
+     *   2. The user browses to the course they've just being given access to.
+     *
+     * Really all this controls is whether the node is shown as expandable or not. It is uber un-important.
+     *
+     * @param stdClass $course
+     * @return bool
+     */
+    protected function can_expand_course($course) {
+        $cache = $this->get_expand_course_cache();
+        $canexpand = $cache->get($course->id);
+        if ($canexpand === false) {
+            $canexpand = isloggedin() && can_access_course($course);
+            $canexpand = (int)$canexpand;
+            $cache->set($course->id, $canexpand);
+        }
+        return ($canexpand === 1);
+    }
+
     /**
      * Returns true if the category has already been loaded as have any child categories
      *
index 41444e4..76b0c83 100644 (file)
@@ -1366,7 +1366,7 @@ class html_writer {
         if (empty($attributes['id'])) {
             $attributes['id'] = self::random_id('ts_');
         }
-        $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, array('id'=>$attributes['id']));
+        $timerselector = self::select($timeunits, $name, $currentdate[$userdatetype], null, $attributes);
         $label = self::tag('label', get_string(substr($type, 0, -1), 'form'), array('for'=>$attributes['id'], 'class'=>'accesshide'));
 
         return $label.$timerselector;
diff --git a/lib/phpexcel/PHPExcel/Shared/Excel5.php b/lib/phpexcel/PHPExcel/Shared/Excel5.php
deleted file mode 100644 (file)
index 3caf675..0000000
+++ /dev/null
@@ -1,317 +0,0 @@
-<?php
-/**
- * PHPExcel
- *
- * Copyright (c) 2006 - 2014 PHPExcel
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   PHPExcel
- * @package    PHPExcel_Shared
- * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
- * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt   LGPL
- * @version    ##VERSION##, ##DATE##
- */
-
-/**
- * PHPExcel_Shared_Excel5
- *
- * @category   PHPExcel
- * @package    PHPExcel_Shared
- * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
- */
-class PHPExcel_Shared_Excel5
-{
-       /**
-        * Get the width of a column in pixels. We use the relationship y = ceil(7x) where
-        * x is the width in intrinsic Excel units (measuring width in number of normal characters)
-        * This holds for Arial 10
-        *
-        * @param PHPExcel_Worksheet $sheet The sheet
-        * @param string $col The column
-        * @return integer The width in pixels
-       */
-       public static function sizeCol($sheet, $col = 'A')
-       {
-               // default font of the workbook
-               $font = $sheet->getParent()->getDefaultStyle()->getFont();
-
-               $columnDimensions = $sheet->getColumnDimensions();
-
-               // first find the true column width in pixels (uncollapsed and unhidden)
-               if ( isset($columnDimensions[$col]) and $columnDimensions[$col]->getWidth() != -1 ) {
-
-                       // then we have column dimension with explicit width
-                       $columnDimension = $columnDimensions[$col];
-                       $width = $columnDimension->getWidth();
-                       $pixelWidth = PHPExcel_Shared_Drawing::cellDimensionToPixels($width, $font);
-
-               } else if ($sheet->getDefaultColumnDimension()->getWidth() != -1) {
-
-                       // then we have default column dimension with explicit width
-                       $defaultColumnDimension = $sheet->getDefaultColumnDimension();
-                       $width = $defaultColumnDimension->getWidth();
-                       $pixelWidth = PHPExcel_Shared_Drawing::cellDimensionToPixels($width, $font);
-
-               } else {
-
-                       // we don't even have any default column dimension. Width depends on default font
-                       $pixelWidth = PHPExcel_Shared_Font::getDefaultColumnWidthByFont($font, true);
-               }
-
-               // now find the effective column width in pixels
-               if (isset($columnDimensions[$col]) and !$columnDimensions[$col]->getVisible()) {
-                       $effectivePixelWidth = 0;
-               } else {
-                       $effectivePixelWidth = $pixelWidth;
-               }
-
-               return $effectivePixelWidth;
-       }
-
-       /**
-        * Convert the height of a cell from user's units to pixels. By interpolation
-        * the relationship is: y = 4/3x. If the height hasn't been set by the user we
-        * use the default value. If the row is hidden we use a value of zero.
-        *
-        * @param PHPExcel_Worksheet $sheet The sheet
-        * @param integer $row The row index (1-based)
-        * @return integer The width in pixels
-        */
-       public static function sizeRow($sheet, $row = 1)
-       {
-               // default font of the workbook
-               $font = $sheet->getParent()->getDefaultStyle()->getFont();
-
-               $rowDimensions = $sheet->getRowDimensions();
-
-               // first find the true row height in pixels (uncollapsed and unhidden)
-               if ( isset($rowDimensions[$row]) and $rowDimensions[$row]->getRowHeight() != -1) {
-
-                       // then we have a row dimension
-                       $rowDimension = $rowDimensions[$row];
-                       $rowHeight = $rowDimension->getRowHeight();
-                       $pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10
-
-               } else if ($sheet->getDefaultRowDimension()->getRowHeight() != -1) {
-
-                       // then we have a default row dimension with explicit height
-                       $defaultRowDimension = $sheet->getDefaultRowDimension();
-                       $rowHeight = $defaultRowDimension->getRowHeight();
-                       $pixelRowHeight = PHPExcel_Shared_Drawing::pointsToPixels($rowHeight);
-
-               } else {
-
-                       // we don't even have any default row dimension. Height depends on default font
-                       $pointRowHeight = PHPExcel_Shared_Font::getDefaultRowHeightByFont($font);
-                       $pixelRowHeight = PHPExcel_Shared_Font::fontSizeToPixels($pointRowHeight);
-
-               }
-
-               // now find the effective row height in pixels
-               if ( isset($rowDimensions[$row]) and !$rowDimensions[$row]->getVisible() ) {
-                       $effectivePixelRowHeight = 0;
-               } else {
-                       $effectivePixelRowHeight = $pixelRowHeight;
-               }
-
-               return $effectivePixelRowHeight;
-       }
-
-       /**
-        * Get the horizontal distance in pixels between two anchors
-        * The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets
-        *
-        * @param PHPExcel_Worksheet $sheet
-        * @param string $startColumn
-        * @param integer $startOffsetX Offset within start cell measured in 1/1024 of the cell width
-        * @param string $endColumn
-        * @param integer $endOffsetX Offset within end cell measured in 1/1024 of the cell width
-        * @return integer Horizontal measured in pixels
-        */
-       public static function getDistanceX(PHPExcel_Worksheet $sheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0)
-       {
-               $distanceX = 0;
-
-               // add the widths of the spanning columns
-               $startColumnIndex = PHPExcel_Cell::columnIndexFromString($startColumn) - 1; // 1-based
-               $endColumnIndex = PHPExcel_Cell::columnIndexFromString($endColumn) - 1; // 1-based
-               for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) {
-                       $distanceX += self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($i));
-               }
-
-               // correct for offsetX in startcell
-               $distanceX -= (int) floor(self::sizeCol($sheet, $startColumn) * $startOffsetX / 1024);
-
-               // correct for offsetX in endcell
-               $distanceX -= (int) floor(self::sizeCol($sheet, $endColumn) * (1 - $endOffsetX / 1024));
-
-               return $distanceX;
-       }
-
-       /**
-        * Get the vertical distance in pixels between two anchors
-        * The distanceY is found as sum of all the spanning rows minus two offsets
-        *
-        * @param PHPExcel_Worksheet $sheet
-        * @param integer $startRow (1-based)
-        * @param integer $startOffsetY Offset within start cell measured in 1/256 of the cell height
-        * @param integer $endRow (1-based)
-        * @param integer $endOffsetY Offset within end cell measured in 1/256 of the cell height
-        * @return integer Vertical distance measured in pixels
-        */
-       public static function getDistanceY(PHPExcel_Worksheet $sheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0)
-       {
-               $distanceY = 0;
-
-               // add the widths of the spanning rows
-               for ($row = $startRow; $row <= $endRow; ++$row) {
-                       $distanceY += self::sizeRow($sheet, $row);
-               }
-
-               // correct for offsetX in startcell
-               $distanceY -= (int) floor(self::sizeRow($sheet, $startRow) * $startOffsetY / 256);
-
-               // correct for offsetX in endcell
-               $distanceY -= (int) floor(self::sizeRow($sheet, $endRow) * (1 - $endOffsetY / 256));
-
-               return $distanceY;
-       }
-
-       /**
-        * Convert 1-cell anchor coordinates to 2-cell anchor coordinates
-        * This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications
-        *
-        * Calculate the vertices that define the position of the image as required by
-        * the OBJ record.
-        *
-        *               +------------+------------+
-        *               |       A        |       B      |
-        *   +-----+------------+------------+
-        *   |   |(x1,y1)        |                      |
-        *   |  1  |(A1)._______|______   |
-        *   |   |      |                         |      |
-        *   |   |      |                         |      |
-        *   +-----+----|       BITMAP  |-----+
-        *   |   |      |                         |      |
-        *   |  2  |    |______________.         |
-        *   |   |                      |               (B2)|
-        *   |   |                      |        (x2,y2)|
-        *   +---- +------------+------------+
-        *
-        * Example of a bitmap that covers some of the area from cell A1 to cell B2.
-        *
-        * Based on the width and height of the bitmap we need to calculate 8 vars:
-        *       $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
-        * The width and height of the cells are also variable and have to be taken into
-        * account.
-        * The values of $col_start and $row_start are passed in from the calling
-        * function. The values of $col_end and $row_end are calculated by subtracting
-        * the width and height of the bitmap from the width and height of the
-        * underlying cells.
-        * The vertices are expressed as a percentage of the underlying cell width as
-        * follows (rhs values are in pixels):
-        *
-        *         x1 = X / W *1024
-        *         y1 = Y / H *256
-        *         x2 = (X-1) / W *1024
-        *         y2 = (Y-1) / H *256
-        *
-        *         Where:  X is distance from the left side of the underlying cell
-        *                         Y is distance from the top of the underlying cell
-        *                         W is the width of the cell
-        *                         H is the height of the cell
-        *
-        * @param PHPExcel_Worksheet $sheet
-        * @param string $coordinates E.g. 'A1'
-        * @param integer $offsetX Horizontal offset in pixels
-        * @param integer $offsetY Vertical offset in pixels
-        * @param integer $width Width in pixels
-        * @param integer $height Height in pixels
-        * @return array
-        */
-       public static function oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height)
-       {
-               list($column, $row) = PHPExcel_Cell::coordinateFromString($coordinates);
-               $col_start = PHPExcel_Cell::columnIndexFromString($column) - 1;
-               $row_start = $row - 1;
-
-               $x1 = $offsetX;
-               $y1 = $offsetY;
-
-               // Initialise end cell to the same as the start cell
-               $col_end        = $col_start;  // Col containing lower right corner of object
-               $row_end        = $row_start;  // Row containing bottom right corner of object
-
-               // Zero the specified offset if greater than the cell dimensions
-               if ($x1 >= self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_start))) {
-                       $x1 = 0;
-               }
-               if ($y1 >= self::sizeRow($sheet, $row_start + 1)) {
-                       $y1 = 0;
-               }
-
-               $width    = $width  + $x1 -1;
-               $height  = $height + $y1 -1;
-
-               // Subtract the underlying cell widths to find the end cell of the image
-               while ($width >= self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end))) {
-                       $width -= self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end));
-                       ++$col_end;
-               }
-
-               // Subtract the underlying cell heights to find the end cell of the image
-               while ($height >= self::sizeRow($sheet, $row_end + 1)) {
-                       $height -= self::sizeRow($sheet, $row_end + 1);
-                       ++$row_end;
-               }
-
-               // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
-               // with zero height or width.
-               if (self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_start)) == 0) {
-                       return;
-               }
-               if (self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end))   == 0) {
-                       return;
-               }
-               if (self::sizeRow($sheet, $row_start + 1) == 0) {
-                       return;
-               }
-               if (self::sizeRow($sheet, $row_end + 1)   == 0) {
-                       return;
-               }
-
-               // Convert the pixel values to the percentage value expected by Excel
-               $x1 = $x1        / self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_start))   * 1024;
-               $y1 = $y1        / self::sizeRow($sheet, $row_start + 1)   *  256;
-               $x2 = ($width + 1)  / self::sizeCol($sheet, PHPExcel_Cell::stringFromColumnIndex($col_end))      * 1024; // Distance to right side of object
-               $y2 = ($height + 1) / self::sizeRow($sheet, $row_end + 1)        *  256; // Distance to bottom of object
-
-               $startCoordinates = PHPExcel_Cell::stringFromColumnIndex($col_start) . ($row_start + 1);
-               $endCoordinates = PHPExcel_Cell::stringFromColumnIndex($col_end) . ($row_end + 1);
-
-               $twoAnchor = array(
-                       'startCoordinates' => $startCoordinates,
-                       'startOffsetX' => $x1,
-                       'startOffsetY' => $y1,
-                       'endCoordinates' => $endCoordinates,
-                       'endOffsetX' => $x2,
-                       'endOffsetY' => $y2,
-               );
-
-               return  $twoAnchor;
-       }
-
-}
diff --git a/lib/phpexcel/PHPExcel/Shared/OLE.php b/lib/phpexcel/PHPExcel/Shared/OLE.php
deleted file mode 100644 (file)
index 9796282..0000000
+++ /dev/null
@@ -1,531 +0,0 @@
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2002 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Author: Xavier Noguer <xnoguer@php.net>                              |
-// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
-// +----------------------------------------------------------------------+
-//
-// $Id: OLE.php,v 1.13 2007/03/07 14:38:25 schmidt Exp $
-
-
-/**
-* Array for storing OLE instances that are accessed from
-* OLE_ChainedBlockStream::stream_open().
-* @var  array
-*/
-$GLOBALS['_OLE_INSTANCES'] = array();
-
-/**
-* OLE package base class.
-*
-* @author   Xavier Noguer <xnoguer@php.net>
-* @author   Christian Schmidt <schmidt@php.net>
-* @category   PHPExcel
-* @package    PHPExcel_Shared_OLE
-*/
-class PHPExcel_Shared_OLE
-{
-       const OLE_PPS_TYPE_ROOT   =      5;
-       const OLE_PPS_TYPE_DIR    =      1;
-       const OLE_PPS_TYPE_FILE   =      2;
-       const OLE_DATA_SIZE_SMALL = 0x1000;
-       const OLE_LONG_INT_SIZE   =      4;
-       const OLE_PPS_SIZE        =   0x80;
-
-       /**
-        * The file handle for reading an OLE container
-        * @var resource
-       */
-       public $_file_handle;
-
-       /**
-       * Array of PPS's found on the OLE container
-       * @var array
-       */
-       public $_list = array();
-
-       /**
-        * Root directory of OLE container
-        * @var OLE_PPS_Root
-       */
-       public $root;
-
-       /**
-        * Big Block Allocation Table
-        * @var array  (blockId => nextBlockId)
-       */
-       public $bbat;
-
-       /**
-        * Short Block Allocation Table
-        * @var array  (blockId => nextBlockId)
-       */
-       public $sbat;
-
-       /**
-        * Size of big blocks. This is usually 512.
-        * @var  int  number of octets per block.
-       */
-       public $bigBlockSize;
-
-       /**
-        * Size of small blocks. This is usually 64.
-        * @var  int  number of octets per block
-       */
-       public $smallBlockSize;
-
-       /**
-        * Reads an OLE container from the contents of the file given.
-        *
-        * @acces public
-        * @param string $file
-        * @return mixed true on success, PEAR_Error on failure
-       */
-       public function read($file)
-       {
-               $fh = fopen($file, "r");
-               if (!$fh) {
-                       throw new PHPExcel_Reader_Exception("Can't open file $file");
-               }
-               $this->_file_handle = $fh;
-
-               $signature = fread($fh, 8);
-               if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
-                       throw new PHPExcel_Reader_Exception("File doesn't seem to be an OLE container.");
-               }
-               fseek($fh, 28);
-               if (fread($fh, 2) != "\xFE\xFF") {
-                       // This shouldn't be a problem in practice
-                       throw new PHPExcel_Reader_Exception("Only Little-Endian encoding is supported.");
-               }
-               // Size of blocks and short blocks in bytes
-               $this->bigBlockSize = pow(2, self::_readInt2($fh));
-               $this->smallBlockSize  = pow(2, self::_readInt2($fh));
-
-               // Skip UID, revision number and version number
-               fseek($fh, 44);
-               // Number of blocks in Big Block Allocation Table
-               $bbatBlockCount = self::_readInt4($fh);
-
-               // Root chain 1st block
-               $directoryFirstBlockId = self::_readInt4($fh);
-
-               // Skip unused bytes
-               fseek($fh, 56);
-               // Streams shorter than this are stored using small blocks
-               $this->bigBlockThreshold = self::_readInt4($fh);
-               // Block id of first sector in Short Block Allocation Table
-               $sbatFirstBlockId = self::_readInt4($fh);
-               // Number of blocks in Short Block Allocation Table
-               $sbbatBlockCount = self::_readInt4($fh);
-               // Block id of first sector in Master Block Allocation Table
-               $mbatFirstBlockId = self::_readInt4($fh);
-               // Number of blocks in Master Block Allocation Table
-               $mbbatBlockCount = self::_readInt4($fh);
-               $this->bbat = array();
-
-               // Remaining 4 * 109 bytes of current block is beginning of Master
-               // Block Allocation Table
-               $mbatBlocks = array();
-               for ($i = 0; $i < 109; ++$i) {
-                       $mbatBlocks[] = self::_readInt4($fh);
-               }
-
-               // Read rest of Master Block Allocation Table (if any is left)
-               $pos = $this->_getBlockOffset($mbatFirstBlockId);
-               for ($i = 0; $i < $mbbatBlockCount; ++$i) {
-                       fseek($fh, $pos);
-                       for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
-                               $mbatBlocks[] = self::_readInt4($fh);
-                       }
-                       // Last block id in each block points to next block
-                       $pos = $this->_getBlockOffset(self::_readInt4($fh));
-               }
-
-               // Read Big Block Allocation Table according to chain specified by
-               // $mbatBlocks
-               for ($i = 0; $i < $bbatBlockCount; ++$i) {
-                       $pos = $this->_getBlockOffset($mbatBlocks[$i]);
-                       fseek($fh, $pos);
-                       for ($j = 0 ; $j < $this->bigBlockSize / 4; ++$j) {
-                               $this->bbat[] = self::_readInt4($fh);
-                       }
-               }
-
-               // Read short block allocation table (SBAT)
-               $this->sbat = array();
-               $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
-               $sbatFh = $this->getStream($sbatFirstBlockId);
-               for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
-                       $this->sbat[$blockId] = self::_readInt4($sbatFh);
-               }
-               fclose($sbatFh);
-
-               $this->_readPpsWks($directoryFirstBlockId);
-
-               return true;
-       }
-
-       /**
-        * @param  int  block id
-        * @param  int  byte offset from beginning of file
-        * @access public
-        */
-       public function _getBlockOffset($blockId)
-       {
-               return 512 + $blockId * $this->bigBlockSize;
-       }
-
-       /**
-       * Returns a stream for use with fread() etc. External callers should
-       * use PHPExcel_Shared_OLE_PPS_File::getStream().
-       * @param   int|PPS   block id or PPS
-       * @return  resource  read-only stream
-       */
-       public function getStream($blockIdOrPps)
-       {
-               static $isRegistered = false;
-               if (!$isRegistered) {
-                       stream_wrapper_register('ole-chainedblockstream',
-                               'PHPExcel_Shared_OLE_ChainedBlockStream');
-                       $isRegistered = true;
-               }
-
-               // Store current instance in global array, so that it can be accessed
-               // in OLE_ChainedBlockStream::stream_open().
-               // Object is removed from self::$instances in OLE_Stream::close().
-               $GLOBALS['_OLE_INSTANCES'][] = $this;
-               $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES']));
-
-               $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
-               if ($blockIdOrPps instanceof PHPExcel_Shared_OLE_PPS) {
-                       $path .= '&blockId=' . $blockIdOrPps->_StartBlock;
-                       $path .= '&size=' . $blockIdOrPps->Size;
-               } else {
-                       $path .= '&blockId=' . $blockIdOrPps;
-               }
-               return fopen($path, 'r');
-       }
-
-       /**
-        * Reads a signed char.
-        * @param   resource  file handle
-        * @return  int
-        * @access public
-        */
-       private static function _readInt1($fh)
-       {
-               list(, $tmp) = unpack("c", fread($fh, 1));
-               return $tmp;
-       }
-
-       /**
-        * Reads an unsigned short (2 octets).
-        * @param   resource  file handle
-        * @return  int
-        * @access public
-        */
-       private static function _readInt2($fh)
-       {
-               list(, $tmp) = unpack("v", fread($fh, 2));
-               return $tmp;
-       }
-
-       /**
-        * Reads an unsigned long (4 octets).
-        * @param   resource  file handle
-        * @return  int
-        * @access public
-        */
-       private static function _readInt4($fh)
-       {
-               list(, $tmp) = unpack("V", fread($fh, 4));
-               return $tmp;
-       }
-
-       /**
-       * Gets information about all PPS's on the OLE container from the PPS WK's
-       * creates an OLE_PPS object for each one.
-       *
-       * @access public
-       * @param  integer  the block id of the first block
-       * @return mixed true on success, PEAR_Error on failure
-       */
-       public function _readPpsWks($blockId)
-       {
-               $fh = $this->getStream($blockId);
-               for ($pos = 0; ; $pos += 128) {
-                       fseek($fh, $pos, SEEK_SET);
-                       $nameUtf16 = fread($fh, 64);
-                       $nameLength = self::_readInt2($fh);
-                       $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
-                       // Simple conversion from UTF-16LE to ISO-8859-1
-                       $name = str_replace("\x00", "", $nameUtf16);
-                       $type = self::_readInt1($fh);
-                       switch ($type) {
-                       case self::OLE_PPS_TYPE_ROOT:
-                               $pps = new PHPExcel_Shared_OLE_PPS_Root(null, null, array());
-                               $this->root = $pps;
-                               break;
-                       case self::OLE_PPS_TYPE_DIR:
-                               $pps = new PHPExcel_Shared_OLE_PPS(null, null, null, null, null,
-                                                                  null, null, null, null, array());
-                               break;
-                       case self::OLE_PPS_TYPE_FILE:
-                               $pps = new PHPExcel_Shared_OLE_PPS_File($name);
-                               break;
-                       default:
-                               continue;
-                       }
-                       fseek($fh, 1, SEEK_CUR);
-                       $pps->Type    = $type;
-                       $pps->Name    = $name;
-                       $pps->PrevPps = self::_readInt4($fh);
-                       $pps->NextPps = self::_readInt4($fh);
-                       $pps->DirPps  = self::_readInt4($fh);
-                       fseek($fh, 20, SEEK_CUR);
-                       $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
-                       $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
-                       $pps->_StartBlock = self::_readInt4($fh);
-                       $pps->Size = self::_readInt4($fh);
-                       $pps->No = count($this->_list);
-                       $this->_list[] = $pps;
-
-                       // check if the PPS tree (starting from root) is complete
-                       if (isset($this->root) &&
-                               $this->_ppsTreeComplete($this->root->No)) {
-
-                               break;
-                       }
-               }
-               fclose($fh);
-
-               // Initialize $pps->children on directories
-               foreach ($this->_list as $pps) {
-                       if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
-                               $nos = array($pps->DirPps);
-                               $pps->children = array();
-                               while ($nos) {
-                                       $no = array_pop($nos);
-                                       if ($no != -1) {
-                                               $childPps = $this->_list[$no];
-                                               $nos[] = $childPps->PrevPps;
-                                               $nos[] = $childPps->NextPps;
-                                               $pps->children[] = $childPps;
-                                       }
-                               }
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-       * It checks whether the PPS tree is complete (all PPS's read)
-       * starting with the given PPS (not necessarily root)
-       *
-       * @access public
-       * @param integer $index The index of the PPS from which we are checking
-       * @return boolean Whether the PPS tree for the given PPS is complete
-       */
-       public function _ppsTreeComplete($index)
-       {
-               return isset($this->_list[$index]) &&
-                          ($pps = $this->_list[$index]) &&
-                          ($pps->PrevPps == -1 ||
-                               $this->_ppsTreeComplete($pps->PrevPps)) &&
-                          ($pps->NextPps == -1 ||
-                               $this->_ppsTreeComplete($pps->NextPps)) &&
-                          ($pps->DirPps == -1 ||
-                               $this->_ppsTreeComplete($pps->DirPps));
-       }
-
-       /**
-       * Checks whether a PPS is a File PPS or not.
-       * If there is no PPS for the index given, it will return false.
-       *
-       * @access public
-       * @param integer $index The index for the PPS
-       * @return bool true if it's a File PPS, false otherwise
-       */
-       public function isFile($index)
-       {
-               if (isset($this->_list[$index])) {
-                       return ($this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE);
-               }
-               return false;
-       }
-
-       /**
-       * Checks whether a PPS is a Root PPS or not.
-       * If there is no PPS for the index given, it will return false.
-       *
-       * @access public
-       * @param integer $index The index for the PPS.
-       * @return bool true if it's a Root PPS, false otherwise
-       */
-       public function isRoot($index)
-       {
-               if (isset($this->_list[$index])) {
-                       return ($this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT);
-               }
-               return false;
-       }
-
-       /**
-       * Gives the total number of PPS's found in the OLE container.
-       *
-       * @access public
-       * @return integer The total number of PPS's found in the OLE container
-       */
-       public function ppsTotal()
-       {
-               return count($this->_list);
-       }
-
-       /**
-       * Gets data from a PPS
-       * If there is no PPS for the index given, it will return an empty string.
-       *
-       * @access public
-       * @param integer $index    The index for the PPS
-       * @param integer $position The position from which to start reading
-       *                          (relative to the PPS)
-       * @param integer $length   The amount of bytes to read (at most)
-       * @return string The binary string containing the data requested
-       * @see OLE_PPS_File::getStream()
-       */
-       public function getData($index, $position, $length)
-       {
-               // if position is not valid return empty string
-               if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
-                       return '';
-               }
-               $fh = $this->getStream($this->_list[$index]);
-               $data = stream_get_contents($fh, $length, $position);
-               fclose($fh);
-               return $data;
-       }
-
-       /**
-       * Gets the data length from a PPS
-       * If there is no PPS for the index given, it will return 0.
-       *
-       * @access public
-       * @param integer $index    The index for the PPS
-       * @return integer The amount of bytes in data the PPS has
-       */
-       public function getDataLength($index)
-       {
-               if (isset($this->_list[$index])) {
-                       return $this->_list[$index]->Size;
-               }
-               return 0;
-       }
-
-       /**
-       * Utility function to transform ASCII text to Unicode
-       *
-       * @access public
-       * @static
-       * @param string $ascii The ASCII string to transform
-       * @return string The string in Unicode
-       */
-       public static function Asc2Ucs($ascii)
-       {
-               $rawname = '';
-               for ($i = 0; $i < strlen($ascii); ++$i) {
-                       $rawname .= $ascii{$i} . "\x00";
-               }
-               return $rawname;
-       }
-
-       /**
-       * Utility function
-       * Returns a string for the OLE container with the date given
-       *
-       * @access public
-       * @static
-       * @param integer $date A timestamp
-       * @return string The string for the OLE container
-       */
-       public static function LocalDate2OLE($date = null)
-       {
-               if (!isset($date)) {
-                       return "\x00\x00\x00\x00\x00\x00\x00\x00";
-               }
-
-               // factor used for separating numbers into 4 bytes parts
-               $factor = pow(2, 32);
-
-               // days from 1-1-1601 until the beggining of UNIX era
-               $days = 134774;
-               // calculate seconds
-               $big_date = $days*24*3600 + gmmktime(date("H",$date),date("i",$date),date("s",$date),
-                                                                                        date("m",$date),date("d",$date),date("Y",$date));
-               // multiply just to make MS happy
-               $big_date *= 10000000;
-
-               $high_part = floor($big_date / $factor);
-               // lower 4 bytes
-               $low_part = floor((($big_date / $factor) - $high_part) * $factor);
-
-               // Make HEX string
-               $res = '';
-
-               for ($i = 0; $i < 4; ++$i) {
-                       $hex = $low_part % 0x100;
-                       $res .= pack('c', $hex);
-                       $low_part /= 0x100;
-               }
-               for ($i = 0; $i < 4; ++$i) {
-                       $hex = $high_part % 0x100;
-                       $res .= pack('c', $hex);
-                       $high_part /= 0x100;
-               }
-               return $res;
-       }
-
-       /**
-       * Returns a timestamp from an OLE container's date
-       *
-       * @access public
-       * @static
-       * @param integer $string A binary string with the encoded date
-       * @return string The timestamp corresponding to the string
-       */
-       public static function OLE2LocalDate($string)
-       {
-               if (strlen($string) != 8) {
-                       return new PEAR_Error("Expecting 8 byte string");
-               }
-
-               // factor used for separating numbers into 4 bytes parts
-               $factor = pow(2,32);
-               list(, $high_part) = unpack('V', substr($string, 4, 4));
-               list(, $low_part) = unpack('V', substr($string, 0, 4));
-
-               $big_date = ($high_part * $factor) + $low_part;
-               // translate to seconds
-               $big_date /= 10000000;
-
-               // days from 1-1-1601 until the beggining of UNIX era
-               $days = 134774;
-
-               // translate to seconds from beggining of UNIX era
-               $big_date -= $days * 24 * 3600;
-               return floor($big_date);
-       }
-}
diff --git a/lib/phpexcel/PHPExcel/Shared/OLE/ChainedBlockStream.php b/lib/phpexcel/PHPExcel/Shared/OLE/ChainedBlockStream.php
deleted file mode 100644 (file)
index 5145206..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-<?php
-/**
- * PHPExcel
- *
- * Copyright (C) 2006 - 2014 PHPExcel
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   PHPExcel
- * @package    PHPExcel_Shared_OLE
- * @copyright  Copyright (c) 2006 - 2007 Christian Schmidt
- * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt   LGPL
- * @version ##VERSION##, ##DATE##
- */
-
-/**
- * PHPExcel_Shared_OLE_ChainedBlockStream
- *
- * Stream wrapper for reading data stored in an OLE file. Implements methods
- * for PHP's stream_wrapper_register(). For creating streams using this
- * wrapper, use PHPExcel_Shared_OLE_PPS_File::getStream().
- *
- * @category   PHPExcel
- * @package    PHPExcel_Shared_OLE
- */
-class PHPExcel_Shared_OLE_ChainedBlockStream
-{
-       /**
-        * The OLE container of the file that is being read.
-        * @var OLE
-        */
-       public $ole;
-
-       /**
-        * Parameters specified by fopen().
-        * @var array
-        */
-       public $params;
-
-       /**
-        * The binary data of the file.
-        * @var  string
-        */
-       public $data;
-
-       /**
-        * The file pointer.
-        * @var  int  byte offset
-        */
-       public $pos;
-
-       /**
-        * Implements support for fopen().
-        * For creating streams using this wrapper, use OLE_PPS_File::getStream().
-        *
-        * @param       string  $path                   resource name including scheme, e.g.
-        *                                                                      ole-chainedblockstream://oleInstanceId=1
-        * @param       string  $mode                   only "r" is supported
-        * @param       int             $options                mask of STREAM_REPORT_ERRORS and STREAM_USE_PATH
-        * @param       string  &$openedPath    absolute path of the opened stream (out parameter)
-        * @return      bool    true on success
-        */
-       public function stream_open($path, $mode, $options, &$openedPath)
-       {
-               if ($mode != 'r') {
-                       if ($options & STREAM_REPORT_ERRORS) {
-                               trigger_error('Only reading is supported', E_USER_WARNING);
-                       }
-                       return false;
-               }
-
-               // 25 is length of "ole-chainedblockstream://"
-               parse_str(substr($path, 25), $this->params);
-               if (!isset($this->params['oleInstanceId'],
-                                  $this->params['blockId'],
-                                  $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) {
-
-                       if ($options & STREAM_REPORT_ERRORS) {
-                               trigger_error('OLE stream not found', E_USER_WARNING);
-                       }
-                       return false;
-               }
-               $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']];
-
-               $blockId = $this->params['blockId'];
-               $this->data = '';
-               if (isset($this->params['size']) &&
-                       $this->params['size'] < $this->ole->bigBlockThreshold &&
-                       $blockId != $this->ole->root->_StartBlock) {
-
-                       // Block id refers to small blocks
-                       $rootPos = $this->ole->_getBlockOffset($this->ole->root->_StartBlock);
-                       while ($blockId != -2) {
-                               $pos = $rootPos + $blockId * $this->ole->bigBlockSize;
-                               $blockId = $this->ole->sbat[$blockId];
-                               fseek($this->ole->_file_handle, $pos);
-                               $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
-                       }
-               } else {
-                       // Block id refers to big blocks
-                       while ($blockId != -2) {
-                               $pos = $this->ole->_getBlockOffset($blockId);
-                               fseek($this->ole->_file_handle, $pos);
-                               $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
-                               $blockId = $this->ole->bbat[$blockId];
-                       }
-               }
-               if (isset($this->params['size'])) {
-                       $this->data = substr($this->data, 0, $this->params['size']);
-               }
-
-               if ($options & STREAM_USE_PATH) {
-                       $openedPath = $path;
-               }
-
-               return true;
-       }
-
-       /**
-        * Implements support for fclose().
-        *
-        */
-       public function stream_close()
-       {
-               $this->ole = null;
-               unset($GLOBALS['_OLE_INSTANCES']);
-       }
-
-       /**
-        * Implements support for fread(), fgets() etc.
-        *
-        * @param   int         $count  maximum number of bytes to read
-        * @return  string
-        */
-       public function stream_read($count)
-       {
-               if ($this->stream_eof()) {
-                       return false;
-               }
-               $s = substr($this->data, $this->pos, $count);
-               $this->pos += $count;
-               return $s;
-       }
-
-       /**
-        * Implements support for feof().
-        *
-        * @return  bool  TRUE if the file pointer is at EOF; otherwise FALSE
-        */
-       public function stream_eof()
-       {
-//             As we don't support below 5.2 anymore, this is simply redundancy and overhead
-//             $eof = $this->pos >= strlen($this->data);
-//             // Workaround for bug in PHP 5.0.x: http://bugs.php.net/27508
-//             if (version_compare(PHP_VERSION, '5.0', '>=') &&
-//                     version_compare(PHP_VERSION, '5.1', '<')) {
-//                $eof = !$eof;
-//             }
-//             return $eof;
-               return $this->pos >= strlen($this->data);
-       }
-
-       /**
-        * Returns the position of the file pointer, i.e. its offset into the file
-        * stream. Implements support for ftell().
-        *
-        * @return  int
-        */
-       public function stream_tell()
-       {
-               return $this->pos;
-       }
-
-       /**
-        * Implements support for fseek().
-        *
-        * @param       int             $offset byte offset
-        * @param       int             $whence SEEK_SET, SEEK_CUR or SEEK_END
-        * @return      bool
-        */
-       public function stream_seek($offset, $whence)
-       {
-               if ($whence == SEEK_SET && $offset >= 0) {
-                       $this->pos = $offset;
-               } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) {
-                       $this->pos += $offset;
-               } elseif ($whence == SEEK_END && -$offset <= sizeof($this->data)) {
-                       $this->pos = strlen($this->data) + $offset;
-               } else {
-                       return false;
-               }
-               return true;
-       }
-
-       /**
-        * Implements support for fstat(). Currently the only supported field is
-        * "size".
-        * @return  array
-        */
-       public function stream_stat()
-       {
-               return array(
-                       'size' => strlen($this->data),
-                       );
-       }
-
-       // Methods used by stream_wrapper_register() that are not implemented:
-       // bool stream_flush ( void )
-       // int stream_write ( string data )
-       // bool rename ( string path_from, string path_to )
-       // bool mkdir ( string path, int mode, int options )
-       // bool rmdir ( string path, int options )
-       // bool dir_opendir ( string path, int options )
-       // array url_stat ( string path, int flags )
-       // string dir_readdir ( void )
-       // bool dir_rewinddir ( void )
-       // bool dir_closedir ( void )
-}
diff --git a/lib/phpexcel/PHPExcel/Shared/OLE/PPS.php b/lib/phpexcel/PHPExcel/Shared/OLE/PPS.php
deleted file mode 100644 (file)
index 4db0ae4..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2002 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Author: Xavier Noguer <xnoguer@php.net>                              |
-// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
-// +----------------------------------------------------------------------+
-//
-// $Id: PPS.php,v 1.7 2007/02/13 21:00:42 schmidt Exp $
-
-
-/**
-* Class for creating PPS's for OLE containers
-*
-* @author   Xavier Noguer <xnoguer@php.net>
-* @category PHPExcel
-* @package  PHPExcel_Shared_OLE
-*/
-class PHPExcel_Shared_OLE_PPS
-{
-       /**
-       * The PPS index
-       * @var integer
-       */
-       public $No;
-
-       /**
-       * The PPS name (in Unicode)
-       * @var string
-       */
-       public $Name;
-
-       /**
-       * The PPS type. Dir, Root or File
-       * @var integer
-       */
-       public $Type;
-
-       /**
-       * The index of the previous PPS
-       * @var integer
-       */
-       public $PrevPps;
-
-       /**
-       * The index of the next PPS
-       * @var integer
-       */
-       public $NextPps;
-
-       /**
-       * The index of it's first child if this is a Dir or Root PPS
-       * @var integer
-       */
-       public $DirPps;
-
-       /**
-       * A timestamp
-       * @var integer
-       */
-       public $Time1st;
-
-       /**
-       * A timestamp
-       * @var integer
-       */
-       public $Time2nd;
-
-       /**
-       * Starting block (small or big) for this PPS's data  inside the container
-       * @var integer
-       */
-       public $_StartBlock;
-
-       /**
-       * The size of the PPS's data (in bytes)
-       * @var integer
-       */
-       public $Size;
-
-       /**
-       * The PPS's data (only used if it's not using a temporary file)
-       * @var string
-       */
-       public $_data;
-
-       /**
-       * Array of child PPS's (only used by Root and Dir PPS's)
-       * @var array
-       */
-       public $children = array();
-
-       /**
-       * Pointer to OLE container
-       * @var OLE
-       */
-       public $ole;
-
-       /**
-       * The constructor
-       *
-       * @access public
-       * @param integer $No   The PPS index
-       * @param string  $name The PPS name
-       * @param integer $type The PPS type. Dir, Root or File
-       * @param integer $prev The index of the previous PPS
-       * @param integer $next The index of the next PPS
-       * @param integer $dir  The index of it's first child if this is a Dir or Root PPS
-       * @param integer $time_1st A timestamp
-       * @param integer $time_2nd A timestamp
-       * @param string  $data  The (usually binary) source data of the PPS
-       * @param array   $children Array containing children PPS for this PPS
-       */
-       public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
-       {
-               $this->No      = $No;
-               $this->Name    = $name;
-               $this->Type    = $type;
-               $this->PrevPps = $prev;
-               $this->NextPps = $next;
-               $this->DirPps  = $dir;
-               $this->Time1st = $time_1st;
-               $this->Time2nd = $time_2nd;
-               $this->_data      = $data;
-               $this->children   = $children;
-               if ($data != '') {
-                       $this->Size = strlen($data);
-               } else {
-                       $this->Size = 0;
-               }
-       }
-
-       /**
-       * Returns the amount of data saved for this PPS
-       *
-       * @access public
-       * @return integer The amount of data (in bytes)
-       */
-       public function _DataLen()
-       {
-               if (!isset($this->_data)) {
-                       return 0;
-               }
-               //if (isset($this->_PPS_FILE)) {
-               //      fseek($this->_PPS_FILE, 0);
-               //      $stats = fstat($this->_PPS_FILE);
-               //      return $stats[7];
-               //} else {
-                       return strlen($this->_data);
-               //}
-       }
-
-       /**
-       * Returns a string with the PPS's WK (What is a WK?)
-       *
-       * @access public
-       * @return string The binary string
-       */
-       public function _getPpsWk()
-       {
-               $ret = str_pad($this->Name,64,"\x00");
-
-               $ret .= pack("v", strlen($this->Name) + 2)  // 66
-                         . pack("c", $this->Type)              // 67
-                         . pack("c", 0x00) //UK                // 68
-                         . pack("V", $this->PrevPps) //Prev    // 72
-                         . pack("V", $this->NextPps) //Next    // 76
-                         . pack("V", $this->DirPps)  //Dir     // 80
-                         . "\x00\x09\x02\x00"                  // 84
-                         . "\x00\x00\x00\x00"                  // 88
-                         . "\xc0\x00\x00\x00"                  // 92
-                         . "\x00\x00\x00\x46"                  // 96 // Seems to be ok only for Root
-                         . "\x00\x00\x00\x00"                  // 100
-                         . PHPExcel_Shared_OLE::LocalDate2OLE($this->Time1st)       // 108
-                         . PHPExcel_Shared_OLE::LocalDate2OLE($this->Time2nd)       // 116
-                         . pack("V", isset($this->_StartBlock)?
-                                               $this->_StartBlock:0)        // 120
-                         . pack("V", $this->Size)               // 124
-                         . pack("V", 0);                        // 128
-               return $ret;
-       }
-
-       /**
-       * Updates index and pointers to previous, next and children PPS's for this
-       * PPS. I don't think it'll work with Dir PPS's.
-       *
-       * @access public
-       * @param array &$raList Reference to the array of PPS's for the whole OLE
-       *                          container
-       * @return integer          The index for this PPS
-       */
-       public static function _savePpsSetPnt(&$raList, $to_save, $depth = 0)
-       {
-               if ( !is_array($to_save) || (empty($to_save)) ) {
-                       return 0xFFFFFFFF;
-               } elseif( count($to_save) == 1 ) {
-                       $cnt = count($raList);
-                       // If the first entry, it's the root... Don't clone it!
-                       $raList[$cnt] = ( $depth == 0 ) ? $to_save[0] : clone $to_save[0];
-                       $raList[$cnt]->No = $cnt;
-                       $raList[$cnt]->PrevPps = 0xFFFFFFFF;
-                       $raList[$cnt]->NextPps = 0xFFFFFFFF;
-                       $raList[$cnt]->DirPps  = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
-               } else {
-                       $iPos  = floor(count($to_save) / 2);
-                       $aPrev = array_slice($to_save, 0, $iPos);
-                       $aNext = array_slice($to_save, $iPos + 1);
-                       $cnt   = count($raList);
-                       // If the first entry, it's the root... Don't clone it!
-                       $raList[$cnt] = ( $depth == 0 ) ? $to_save[$iPos] : clone $to_save[$iPos];
-                       $raList[$cnt]->No = $cnt;
-                       $raList[$cnt]->PrevPps = self::_savePpsSetPnt($raList, $aPrev, $depth++);
-                       $raList[$cnt]->NextPps = self::_savePpsSetPnt($raList, $aNext, $depth++);
-                       $raList[$cnt]->DirPps  = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
-
-               }
-               return $cnt;
-       }
-}
diff --git a/lib/phpexcel/PHPExcel/Shared/OLE/PPS/File.php b/lib/phpexcel/PHPExcel/Shared/OLE/PPS/File.php
deleted file mode 100644 (file)
index f061f56..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2002 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Author: Xavier Noguer <xnoguer@php.net>                              |
-// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
-// +----------------------------------------------------------------------+
-//
-// $Id: File.php,v 1.11 2007/02/13 21:00:42 schmidt Exp $
-
-
-/**
-* Class for creating File PPS's for OLE containers
-*
-* @author   Xavier Noguer <xnoguer@php.net>
-* @category PHPExcel
-* @package  PHPExcel_Shared_OLE
-*/
-class PHPExcel_Shared_OLE_PPS_File extends PHPExcel_Shared_OLE_PPS
-       {
-       /**
-       * The constructor
-       *
-       * @access public
-       * @param string $name The name of the file (in Unicode)
-       * @see OLE::Asc2Ucs()
-       */
-       public function __construct($name)
-       {
-               parent::__construct(
-                       null,
-                       $name,
-                       PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE,
-                       null,
-                       null,
-                       null,
-                       null,
-                       null,
-                       '',
-                       array());
-       }
-
-       /**
-       * Initialization method. Has to be called right after OLE_PPS_File().
-       *
-       * @access public
-       * @return mixed true on success
-       */
-       public function init()
-       {
-               return true;
-       }
-
-       /**
-       * Append data to PPS
-       *
-       * @access public
-       * @param string $data The data to append
-       */
-       public function append($data)
-       {
-               $this->_data .= $data;
-       }
-
-       /**
-        * Returns a stream for reading this file using fread() etc.
-        * @return  resource  a read-only stream
-        */
-       public function getStream()
-       {
-               $this->ole->getStream($this);
-       }
-}
diff --git a/lib/phpexcel/PHPExcel/Shared/OLE/PPS/Root.php b/lib/phpexcel/PHPExcel/Shared/OLE/PPS/Root.php
deleted file mode 100644 (file)
index eb929d2..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2002 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Author: Xavier Noguer <xnoguer@php.net>                              |
-// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
-// +----------------------------------------------------------------------+
-//
-// $Id: Root.php,v 1.9 2005/04/23 21:53:49 dufuz Exp $
-
-
-/**
-* Class for creating Root PPS's for OLE containers
-*
-* @author   Xavier Noguer <xnoguer@php.net>
-* @category PHPExcel
-* @package  PHPExcel_Shared_OLE
-*/
-class PHPExcel_Shared_OLE_PPS_Root extends PHPExcel_Shared_OLE_PPS
-       {
-
-       /**
-        * Directory for temporary files
-        * @var string
-        */
-       protected $_tmp_dir             = NULL;
-
-       /**
-        * @param integer $time_1st A timestamp
-        * @param integer $time_2nd A timestamp
-        */
-       public function __construct($time_1st, $time_2nd, $raChild)
-       {
-               $this->_tempDir = PHPExcel_Shared_File::sys_get_temp_dir();
-
-               parent::__construct(
-                  null,
-                  PHPExcel_Shared_OLE::Asc2Ucs('Root Entry'),
-                  PHPExcel_Shared_OLE::OLE_PPS_TYPE_ROOT,
-                  null,
-                  null,
-                  null,
-                  $time_1st,
-                  $time_2nd,
-                  null,
-                  $raChild);
-       }
-
-       /**
-       * Method for saving the whole OLE container (including files).
-       * In fact, if called with an empty argument (or '-'), it saves to a
-       * temporary file and then outputs it's contents to stdout.
-       * If a resource pointer to a stream created by fopen() is passed
-       * it will be used, but you have to close such stream by yourself.
-       *
-       * @param string|resource $filename The name of the file or stream where to save the OLE container.
-       * @access public
-       * @return mixed true on success
-       */
-       public function save($filename)
-       {
-               // Initial Setting for saving
-               $this->_BIG_BLOCK_SIZE  = pow(2,
-                                         ((isset($this->_BIG_BLOCK_SIZE))? self::_adjust2($this->_BIG_BLOCK_SIZE)  : 9));
-               $this->_SMALL_BLOCK_SIZE= pow(2,
-                                         ((isset($this->_SMALL_BLOCK_SIZE))?  self::_adjust2($this->_SMALL_BLOCK_SIZE): 6));
-
-               if (is_resource($filename)) {
-                   $this->_FILEH_ = $filename;
-               } else if ($filename == '-' || $filename == '') {\r
-                       if ($this->_tmp_dir === NULL)\r
-                               $this->_tmp_dir = PHPExcel_Shared_File::sys_get_temp_dir();\r
-                       $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root");\r
-                       $this->_FILEH_ = fopen($this->_tmp_filename,"w+b");\r
-                       if ($this->_FILEH_ == false) {\r
-                               throw new PHPExcel_Writer_Exception("Can't create temporary file.");\r
-                       }
-               } else {
-                       $this->_FILEH_ = fopen($filename, "wb");
-               }
-               if ($this->_FILEH_ == false) {
-                       throw new PHPExcel_Writer_Exception("Can't open $filename. It may be in use or protected.");
-               }
-               // Make an array of PPS's (for Save)
-               $aList = array();
-               PHPExcel_Shared_OLE_PPS::_savePpsSetPnt($aList, array($this));\r
-               // calculate values for header
-               list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo);
-               // Save Header
-               $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
-
-               // Make Small Data string (write SBD)
-               $this->_data = $this->_makeSmallData($aList);
-
-               // Write BB
-               $this->_saveBigData($iSBDcnt, $aList);
-               // Write PPS
-               $this->_savePps($aList);
-               // Write Big Block Depot and BDList and Adding Header informations
-               $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);
-
-               if (!is_resource($filename)) {
-                       fclose($this->_FILEH_);
-               }
-
-               return true;
-       }
-
-       /**
-       * Calculate some numbers
-       *
-       * @access public
-       * @param array $raList Reference to an array of PPS's
-       * @return array The array of numbers
-       */
-       public function _calcSize(&$raList)
-       {
-               // Calculate Basic Setting
-               list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0);
-               $iSmallLen = 0;
-               $iSBcnt = 0;
-               $iCount = count($raList);
-               for ($i = 0; $i < $iCount; ++$i) {
-                       if ($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE) {
-                               $raList[$i]->Size = $raList[$i]->_DataLen();
-                               if ($raList[$i]->Size < PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) {
-                                       $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE)
-                                                                 + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0);
-                               } else {
-                                       $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) +
-                                               (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0));
-                               }
-                       }
-               }
-               $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE;
-               $iSlCnt = floor($this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE);
-               $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0);
-               $iBBcnt +=  (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) +
-                                         (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0));
-               $iCnt = count($raList);
-               $iBdCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_PPS_SIZE;
-               $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0));
-
-               return array($iSBDcnt, $iBBcnt, $iPPScnt);
-       }
-
-       /**
-       * Helper function for caculating a magic value for block sizes
-       *
-       * @access public
-       * @param integer $i2 The argument
-       * @see save()
-       * @return integer
-       */
-       private static function _adjust2($i2)
-       {
-               $iWk = log($i2)/log(2);
-               return ($iWk > floor($iWk))? floor($iWk)+1:$iWk;
-       }
-
-       /**
-       * Save OLE header
-       *
-       * @access public
-       * @param integer $iSBDcnt
-       * @param integer $iBBcnt
-       * @param integer $iPPScnt
-       */
-       public function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt)
-       {
-               $FILE = $this->_FILEH_;
-
-               // Calculate Basic Setting
-               $iBlCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
-               $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
-
-               $iBdExL = 0;
-               $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
-               $iAllW = $iAll;
-               $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
-               $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
-
-               // Calculate BD count
-               if ($iBdCnt > $i1stBdL) {
-                       while (1) {
-                               ++$iBdExL;
-                               ++$iAllW;
-                               $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0);
-                               $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0);
-                               if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) {
-                                       break;
-                               }
-                       }
-               }
-
-               // Save Header
-               fwrite($FILE,
-                               "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
-                               . "\x00\x00\x00\x00"
-                               . "\x00\x00\x00\x00"
-                               . "\x00\x00\x00\x00"
-                               . "\x00\x00\x00\x00"
-                               . pack("v", 0x3b)
-                               . pack("v", 0x03)
-                               . pack("v", -2)
-                               . pack("v", 9)
-                               . pack("v", 6)
-                               . pack("v", 0)
-                               . "\x00\x00\x00\x00"
-                               . "\x00\x00\x00\x00"
-                               . pack("V", $iBdCnt)
-                               . pack("V", $iBBcnt+$iSBDcnt) //ROOT START
-                               . pack("V", 0)
-                               . pack("V", 0x1000)
-                               . pack("V", $iSBDcnt ? 0 : -2)                  //Small Block Depot
-                               . pack("V", $iSBDcnt)
-                 );
-               // Extra BDList Start, Count
-               if ($iBdCnt < $i1stBdL) {
-                       fwrite($FILE,
-                                       pack("V", -2)      // Extra BDList Start
-                                       . pack("V", 0)        // Extra BDList Count
-                                 );
-               } else {
-                       fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL));
-               }
-
-               // BDList
-               for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
-                       fwrite($FILE, pack("V", $iAll+$i));
-               }
-               if ($i < $i1stBdL) {
-                       $jB = $i1stBdL - $i;
-                       for ($j = 0; $j < $jB; ++$j) {
-                               fwrite($FILE, (pack("V", -1)));
-                       }
-               }
-       }
-
-       /**
-       * Saving big data (PPS's with data bigger than PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL)
-       *
-       * @access public
-       * @param integer $iStBlk
-       * @param array &$raList Reference to array of PPS's
-       */
-       public function _saveBigData($iStBlk, &$raList)
-       {
-               $FILE = $this->_FILEH_;
-
-               // cycle through PPS's
-               $iCount = count($raList);
-               for ($i = 0; $i < $iCount; ++$i) {
-                       if ($raList[$i]->Type != PHPExcel_Shared_OLE::OLE_PPS_TYPE_DIR) {
-                               $raList[$i]->Size = $raList[$i]->_DataLen();
-                               if (($raList[$i]->Size >= PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) ||
-                                       (($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data)))
-                               {
-                                       // Write Data
-                                       //if (isset($raList[$i]->_PPS_FILE)) {
-                                       //      $iLen = 0;
-                                       //      fseek($raList[$i]->_PPS_FILE, 0); // To The Top
-                                       //      while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) {
-                                       //              $iLen += strlen($sBuff);
-                                       //              fwrite($FILE, $sBuff);
-                                       //      }
-                                       //} else {
-                                               fwrite($FILE, $raList[$i]->_data);
-                                       //}
-
-                                       if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) {
-                                               fwrite($FILE, str_repeat("\x00", $this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)));
-                                       }
-                                       // Set For PPS
-                                       $raList[$i]->_StartBlock = $iStBlk;
-                                       $iStBlk +=
-                                                       (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) +
-                                                               (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0));
-                               }
-                               // Close file for each PPS, and unlink it
-                               //if (isset($raList[$i]->_PPS_FILE)) {
-                               //      fclose($raList[$i]->_PPS_FILE);
-                               //      $raList[$i]->_PPS_FILE = null;
-                               //      unlink($raList[$i]->_tmp_filename);
-                               //}
-                       }
-               }
-       }
-
-       /**
-       * get small data (PPS's with data smaller than PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL)
-       *
-       * @access public
-       * @param array &$raList Reference to array of PPS's
-       */
-       public function _makeSmallData(&$raList)
-       {
-               $sRes = '';
-               $FILE = $this->_FILEH_;
-               $iSmBlk = 0;
-
-               $iCount = count($raList);
-               for ($i = 0; $i < $iCount; ++$i) {
-                       // Make SBD, small data string
-                       if ($raList[$i]->Type == PHPExcel_Shared_OLE::OLE_PPS_TYPE_FILE) {
-                               if ($raList[$i]->Size <= 0) {
-                                       continue;
-                               }
-                               if ($raList[$i]->Size < PHPExcel_Shared_OLE::OLE_DATA_SIZE_SMALL) {
-                                       $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE)
-                                                                 + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0);
-                                       // Add to SBD
-                                       $jB = $iSmbCnt - 1;
-                                       for ($j = 0; $j < $jB; ++$j) {
-                                               fwrite($FILE, pack("V", $j+$iSmBlk+1));
-                                       }
-                                       fwrite($FILE, pack("V", -2));
-
-                                       //// Add to Data String(this will be written for RootEntry)
-                                       //if ($raList[$i]->_PPS_FILE) {
-                                       //      fseek($raList[$i]->_PPS_FILE, 0); // To The Top
-                                       //      while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) {
-                                       //              $sRes .= $sBuff;
-                                       //      }
-                                       //} else {
-                                               $sRes .= $raList[$i]->_data;
-                                       //}
-                                       if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) {
-                                               $sRes .= str_repeat("\x00",$this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE));
-                                       }
-                                       // Set for PPS
-                                       $raList[$i]->_StartBlock = $iSmBlk;
-                                       $iSmBlk += $iSmbCnt;
-                               }
-                       }
-               }
-               $iSbCnt = floor($this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE);
-               if ($iSmBlk % $iSbCnt) {
-                       $iB = $iSbCnt - ($iSmBlk % $iSbCnt);
-                       for ($i = 0; $i < $iB; ++$i) {
-                               fwrite($FILE, pack("V", -1));
-                       }
-               }
-               return $sRes;
-       }
-
-       /**
-       * Saves all the PPS's WKs
-       *
-       * @access public
-       * @param array $raList Reference to an array with all PPS's
-       */
-       public function _savePps(&$raList)
-       {
-               // Save each PPS WK
-               $iC = count($raList);
-               for ($i = 0; $i < $iC; ++$i) {
-                       fwrite($this->_FILEH_, $raList[$i]->_getPpsWk());
-               }
-               // Adjust for Block
-               $iCnt = count($raList);
-               $iBCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_PPS_SIZE;
-               if ($iCnt % $iBCnt) {
-                       fwrite($this->_FILEH_, str_repeat("\x00",($iBCnt - ($iCnt % $iBCnt)) * PHPExcel_Shared_OLE::OLE_PPS_SIZE));
-               }
-       }
-
-       /**
-       * Saving Big Block Depot
-       *
-       * @access public
-       * @param integer $iSbdSize
-       * @param integer $iBsize
-       * @param integer $iPpsCnt
-       */
-       public function _saveBbd($iSbdSize, $iBsize, $iPpsCnt)
-       {
-               $FILE = $this->_FILEH_;
-               // Calculate Basic Setting
-               $iBbCnt = $this->_BIG_BLOCK_SIZE / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
-               $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / PHPExcel_Shared_OLE::OLE_LONG_INT_SIZE;
-
-               $iBdExL = 0;
-               $iAll = $iBsize + $iPpsCnt + $iSbdSize;
-               $iAllW = $iAll;
-               $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
-               $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
-               // Calculate BD count
-               if ($iBdCnt >$i1stBdL) {
-                       while (1) {
-                               ++$iBdExL;
-                               ++$iAllW;
-                               $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0);
-                               $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
-                               if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) {
-                                       break;
-                               }
-                       }
-               }
-
-               // Making BD
-               // Set for SBD
-               if ($iSbdSize > 0) {
-                       for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
-                               fwrite($FILE, pack("V", $i+1));
-                       }
-                       fwrite($FILE, pack("V", -2));
-               }
-               // Set for B
-               for ($i = 0; $i < ($iBsize - 1); ++$i) {
-                       fwrite($FILE, pack("V", $i+$iSbdSize+1));
-               }
-               fwrite($FILE, pack("V", -2));
-
-               // Set for PPS
-               for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
-                       fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1));
-               }
-               fwrite($FILE, pack("V", -2));
-               // Set for BBD itself ( 0xFFFFFFFD : BBD)
-               for ($i = 0; $i < $iBdCnt; ++$i) {
-                       fwrite($FILE, pack("V", 0xFFFFFFFD));
-               }
-               // Set for ExtraBDList
-               for ($i = 0; $i < $iBdExL; ++$i) {
-                       fwrite($FILE, pack("V", 0xFFFFFFFC));
-               }
-               // Adjust for Block
-               if (($iAllW + $iBdCnt) % $iBbCnt) {
-                       $iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt));
-                       for ($i = 0; $i < $iBlock; ++$i) {
-                               fwrite($FILE, pack("V", -1));
-                       }
-               }
-               // Extra BDList
-               if ($iBdCnt > $i1stBdL) {
-                       $iN=0;
-                       $iNb=0;
-                       for ($i = $i1stBdL;$i < $iBdCnt; $i++, ++$iN) {
-                               if ($iN >= ($iBbCnt - 1)) {
-                                       $iN = 0;
-                                       ++$iNb;
-                                       fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb));
-                               }
-                               fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i));
-                       }
-                       if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) {
-                               $iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1));
-                               for ($i = 0; $i < $iB; ++$i) {
-                                       fwrite($FILE, pack("V", -1));
-                               }
-                       }
-                       fwrite($FILE, pack("V", -2));
-               }
-       }
-}
diff --git a/lib/phpexcel/PHPExcel/Shared/OLERead.php b/lib/phpexcel/PHPExcel/Shared/OLERead.php
deleted file mode 100644 (file)
index 261bdde..0000000
+++ /dev/null
@@ -1,317 +0,0 @@
-<?php
-/**
- * PHPExcel
- *
- * Copyright (c) 2006 - 2014 PHPExcel
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   PHPExcel
- * @package    PHPExcel_Shared
- * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
- * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt   LGPL
- * @version    ##VERSION##, ##DATE##
- */
-
-defined('IDENTIFIER_OLE') ||
-    define('IDENTIFIER_OLE', pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1));
-
-class PHPExcel_Shared_OLERead {
-       private $data = '';
-
-       // OLE identifier
-       const IDENTIFIER_OLE = IDENTIFIER_OLE;
-
-       // Size of a sector = 512 bytes
-       const BIG_BLOCK_SIZE                                    = 0x200;
-
-       // Size of a short sector = 64 bytes
-       const SMALL_BLOCK_SIZE                                  = 0x40;
-
-       // Size of a directory entry always = 128 bytes
-       const PROPERTY_STORAGE_BLOCK_SIZE               = 0x80;
-
-       // Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
-       const SMALL_BLOCK_THRESHOLD                             = 0x1000;
-
-       // header offsets
-       const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS    = 0x2c;
-       const ROOT_START_BLOCK_POS                              = 0x30;
-       const SMALL_BLOCK_DEPOT_BLOCK_POS               = 0x3c;
-       const EXTENSION_BLOCK_POS                               = 0x44;
-       const NUM_EXTENSION_BLOCK_POS                   = 0x48;
-       const BIG_BLOCK_DEPOT_BLOCKS_POS                = 0x4c;
-
-       // property storage offsets (directory offsets)
-       const SIZE_OF_NAME_POS                                  = 0x40;
-       const TYPE_POS                                                  = 0x42;
-       const START_BLOCK_POS                                   = 0x74;
-       const SIZE_POS                                                  = 0x78;
-
-
-
-       public $wrkbook                                         = null;
-       public $summaryInformation                      = null;
-       public $documentSummaryInformation      = null;
-
-
-       /**
-        * Read the file
-        *
-        * @param $sFileName string Filename
-        * @throws PHPExcel_Reader_Exception
-        */
-       public function read($sFileName)
-       {
-               // Check if file exists and is readable
-               if(!is_readable($sFileName)) {
-                       throw new PHPExcel_Reader_Exception("Could not open " . $sFileName . " for reading! File does not exist, or it is not readable.");
-               }
-
-               // Get the file identifier
-               // Don't bother reading the whole file until we know it's a valid OLE file
-               $this->data = file_get_contents($sFileName, FALSE, NULL, 0, 8);
-
-               // Check OLE identifier
-               if ($this->data != self::IDENTIFIER_OLE) {
-                       throw new PHPExcel_Reader_Exception('The filename ' . $sFileName . ' is not recognised as an OLE file');
-               }
-
-               // Get the file data
-               $this->data = file_get_contents($sFileName);
-
-               // Total number of sectors used for the SAT
-               $this->numBigBlockDepotBlocks = self::_GetInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
-
-               // SecID of the first sector of the directory stream
-               $this->rootStartBlock = self::_GetInt4d($this->data, self::ROOT_START_BLOCK_POS);
-
-               // SecID of the first sector of the SSAT (or -2 if not extant)
-               $this->sbdStartBlock = self::_GetInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
-
-               // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
-               $this->extensionBlock = self::_GetInt4d($this->data, self::EXTENSION_BLOCK_POS);
-
-               // Total number of sectors used by MSAT
-               $this->numExtensionBlocks = self::_GetInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
-
-               $bigBlockDepotBlocks = array();
-               $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
-
-               $bbdBlocks = $this->numBigBlockDepotBlocks;
-
-               if ($this->numExtensionBlocks != 0) {
-                       $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS)/4;
-               }
-
-               for ($i = 0; $i < $bbdBlocks; ++$i) {
-                         $bigBlockDepotBlocks[$i] = self::_GetInt4d($this->data, $pos);
-                         $pos += 4;
-               }
-
-               for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
-                       $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
-                       $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
-
-                       for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
-                               $bigBlockDepotBlocks[$i] = self::_GetInt4d($this->data, $pos);
-                               $pos += 4;
-                       }
-
-                       $bbdBlocks += $blocksToRead;
-                       if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
-                               $this->extensionBlock = self::_GetInt4d($this->data, $pos);
-                       }
-               }
-
-               $pos = 0;
-               $this->bigBlockChain = '';
-               $bbs = self::BIG_BLOCK_SIZE / 4;
-               for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
-                       $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
-
-                       $this->bigBlockChain .= substr($this->data, $pos, 4*$bbs);
-                       $pos += 4*$bbs;
-               }
-
-               $pos = 0;
-               $sbdBlock = $this->sbdStartBlock;
-               $this->smallBlockChain = '';
-               while ($sbdBlock != -2) {
-                       $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
-
-                       $this->smallBlockChain .= substr($this->data, $pos, 4*$bbs);
-                       $pos += 4*$bbs;
-
-                       $sbdBlock = self::_GetInt4d($this->bigBlockChain, $sbdBlock*4);
-               }
-
-               // read the directory stream
-               $block = $this->rootStartBlock;
-               $this->entry = $this->_readData($block);
-
-               $this->_readPropertySets();
-       }
-
-       /**
-        * Extract binary stream data
-        *
-        * @return string
-        */
-       public function getStream($stream)
-       {
-               if ($stream === NULL) {
-                       return null;
-               }
-
-               $streamData = '';
-
-               if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
-                       $rootdata = $this->_readData($this->props[$this->rootentry]['startBlock']);
-
-                       $block = $this->props[$stream]['startBlock'];
-
-                       while ($block != -2) {
-                               $pos = $block * self::SMALL_BLOCK_SIZE;
-                               $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
-
-                               $block = self::_GetInt4d($this->smallBlockChain, $block*4);
-                       }
-
-                       return $streamData;
-               } else {
-                       $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
-                       if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
-                               ++$numBlocks;
-                       }
-
-                       if ($numBlocks == 0) return '';
-
-                       $block = $this->props[$stream]['startBlock'];
-
-                       while ($block != -2) {
-                               $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
-                               $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
-                               $block = self::_GetInt4d($this->bigBlockChain, $block*4);
-                       }
-
-                       return $streamData;
-               }
-       }
-
-       /**
-        * Read a standard stream (by joining sectors using information from SAT)
-        *
-        * @param int $bl Sector ID where the stream starts
-        * @return string Data for standard stream
-        */
-       private function _readData($bl)
-       {
-               $block = $bl;
-               $data = '';
-
-               while ($block != -2)  {
-                       $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
-                       $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
-                       $block = self::_GetInt4d($this->bigBlockChain, $block*4);
-               }
-               return $data;
-        }
-
-       /**
-        * Read entries in the directory stream.
-        */
-       private function _readPropertySets() {
-               $offset = 0;
-
-               // loop through entires, each entry is 128 bytes
-               $entryLen = strlen($this->entry);
-               while ($offset < $entryLen) {
-                       // entry data (128 bytes)
-                       $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
-
-                       // size in bytes of name
-                       $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS+1]) << 8);
-
-                       // type of entry
-                       $type = ord($d[self::TYPE_POS]);
-
-                       // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
-                       // sectorID of first sector of the short-stream container stream, if this entry is root entry
-                       $startBlock = self::_GetInt4d($d, self::START_BLOCK_POS);
-
-                       $size = self::_GetInt4d($d, self::SIZE_POS);
-
-                       $name = str_replace("\x00", "", substr($d,0,$nameSize));
-
-
-                       $this->props[] = array (
-                               'name' => $name,
-                               'type' => $type,
-                               'startBlock' => $startBlock,
-                      &nb