Merge branch 'MDL-44278' of git://github.com/timhunt/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 4 Mar 2014 07:43:23 +0000 (15:43 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 4 Mar 2014 07:43:23 +0000 (15:43 +0800)
353 files changed:
admin/settings/server.php
admin/tests/behat/display_short_names.feature
admin/tests/behat/filter_users.feature
admin/tests/behat/upload_users.feature
admin/tool/behat/lang/en/tool_behat.php
admin/tool/behat/renderer.php
admin/tool/behat/tests/behat/basic_actions.feature
admin/tool/behat/tests/behat/data_generators.feature
admin/tool/behat/tests/behat/edit_permissions.feature
admin/tool/behat/tests/behat/get_and_set_fields.feature [new file with mode: 0644]
admin/tool/behat/tests/behat/list_steps.feature
admin/tool/behat/tests/behat/manipulate_forms.feature
admin/tool/behat/tests/behat/nasty_strings.feature
admin/tool/behat/upgrade.txt
admin/tool/log/classes/helper/buffered_writer.php [new file with mode: 0644]
admin/tool/log/classes/helper/reader.php [new file with mode: 0644]
admin/tool/log/classes/helper/store.php [new file with mode: 0644]
admin/tool/log/classes/log/manager.php [new file with mode: 0644]
admin/tool/log/classes/log/observer.php [new file with mode: 0644]
admin/tool/log/classes/log/store.php [new file with mode: 0644]
admin/tool/log/classes/log/writer.php [new file with mode: 0644]
admin/tool/log/classes/plugininfo/logstore.php [new file with mode: 0644]
admin/tool/log/classes/setting_managestores.php [new file with mode: 0644]
admin/tool/log/db/events.php [new file with mode: 0644]
admin/tool/log/db/install.php [new file with mode: 0644]
admin/tool/log/db/subplugins.php [new file with mode: 0644]
admin/tool/log/lang/en/tool_log.php [new file with mode: 0644]
admin/tool/log/lib.php [new file with mode: 0644]
admin/tool/log/settings.php [new file with mode: 0644]
admin/tool/log/store/database/classes/helper.php [new file with mode: 0644]
admin/tool/log/store/database/classes/log/store.php [new file with mode: 0644]
admin/tool/log/store/database/lang/en/logstore_database.php [new file with mode: 0644]
admin/tool/log/store/database/settings.php [new file with mode: 0644]
admin/tool/log/store/database/test_settings.php [new file with mode: 0644]
admin/tool/log/store/database/tests/fixtures/event.php [new file with mode: 0644]
admin/tool/log/store/database/tests/store_test.php [new file with mode: 0644]
admin/tool/log/store/database/version.php [new file with mode: 0644]
admin/tool/log/store/legacy/classes/event/legacy_logged.php [new file with mode: 0644]
admin/tool/log/store/legacy/classes/log/store.php [new file with mode: 0644]
admin/tool/log/store/legacy/db/access.php [new file with mode: 0644]
admin/tool/log/store/legacy/lang/en/logstore_legacy.php [new file with mode: 0644]
admin/tool/log/store/legacy/settings.php [new file with mode: 0644]
admin/tool/log/store/legacy/tests/fixtures/event.php [new file with mode: 0644]
admin/tool/log/store/legacy/tests/store_test.php [new file with mode: 0644]
admin/tool/log/store/legacy/version.php [new file with mode: 0644]
admin/tool/log/store/standard/classes/log/store.php [new file with mode: 0644]
admin/tool/log/store/standard/db/access.php [new file with mode: 0644]
admin/tool/log/store/standard/db/install.xml [new file with mode: 0644]
admin/tool/log/store/standard/lang/en/logstore_standard.php [new file with mode: 0644]
admin/tool/log/store/standard/settings.php [new file with mode: 0644]
admin/tool/log/store/standard/tests/fixtures/event.php [new file with mode: 0644]
admin/tool/log/store/standard/tests/store_test.php [new file with mode: 0644]
admin/tool/log/store/standard/version.php [new file with mode: 0644]
admin/tool/log/stores.php [new file with mode: 0644]
admin/tool/log/tests/manager_test.php [new file with mode: 0644]
admin/tool/log/version.php [new file with mode: 0644]
admin/tool/uploadcourse/tests/behat/create.feature
admin/tool/uploadcourse/tests/behat/update.feature
auth/tests/behat/behat_auth.php
auth/tests/behat/login.feature
backup/moodle2/restore_stepslib.php
backup/util/ui/tests/behat/duplicate_activities.feature
backup/util/ui/tests/behat/restore_moodle2_courses.feature
badges/classes/observer.php
badges/tests/behat/add_badge.feature
badges/tests/behat/award_badge.feature
blocks/tests/behat/behat_blocks.php
blocks/tests/behat/configure_block_throughout_site.feature
blocks/tests/behat/manage_blocks.feature
blocks/tests/behat/return_block_original_state.feature
blog/tests/behat/comment.feature
calendar/tests/behat/behat_calendar.php
calendar/tests/behat/calendar.feature
cohort/lib.php
cohort/tests/behat/add_cohort.feature
cohort/tests/behat/behat_cohort.php
completion/tests/behat/enable_manual_complete_mark.feature
completion/tests/behat/restrict_activity_by_date.feature
completion/tests/behat/restrict_activity_by_grade.feature
completion/tests/behat/restrict_section_availability.feature
course/format/renderer.php
course/tests/behat/activities_group_icons.feature
course/tests/behat/add_activities.feature
course/tests/behat/behat_course.php
course/tests/behat/category_management.feature
course/tests/behat/category_resort.feature
course/tests/behat/course_category_management_listing.feature
course/tests/behat/course_controls.feature
course/tests/behat/create_delete_course.feature
course/tests/behat/edit_settings.feature
course/tests/behat/force_group_mode.feature
course/tests/behat/max_number_sections.feature
course/tests/behat/move_activities.feature
course/tests/behat/move_sections.feature
course/tests/behat/rename_roles.feature
course/tests/courselib_test.php
enrol/guest/tests/behat/guest_access.feature
enrol/imsenterprise/importnow.php
enrol/imsenterprise/settings.php
enrol/self/tests/behat/self_enrolment.feature
enrol/tests/behat/behat_enrol.php
filter/mediaplugin/filter.php
grade/grading/form/rubric/tests/behat/behat_gradingform_rubric.php
grade/grading/form/rubric/tests/behat/edit_rubric.feature
grade/grading/form/rubric/tests/behat/publish_rubric_templates.feature
grade/grading/form/rubric/tests/behat/reuse_own_rubrics.feature
grade/grading/tests/behat/behat_grading.php
grade/tests/behat/behat_grade.php
grade/tests/behat/grade_view.feature
group/externallib.php
group/tests/behat/auto_creation.feature
group/tests/behat/create_groups.feature
group/tests/behat/delete_groups.feature
group/tests/behat/groups_import.feature
group/tests/behat/id_uniqueness.feature
group/tests/behat/update_groups.feature
lang/en/admin.php
lang/en/moodle.php
lib/accesslib.php
lib/behat/behat_field_manager.php
lib/behat/form_field/behat_form_checkbox.php
lib/behat/form_field/behat_form_date_selector.php
lib/behat/form_field/behat_form_date_time_selector.php
lib/behat/form_field/behat_form_editor.php
lib/behat/form_field/behat_form_field.php
lib/behat/form_field/behat_form_group.php [new file with mode: 0644]
lib/behat/form_field/behat_form_modvisible.php
lib/behat/form_field/behat_form_radio.php
lib/behat/form_field/behat_form_select.php
lib/behat/form_field/behat_form_selectyesno.php
lib/behat/form_field/behat_form_text.php [new file with mode: 0644]
lib/behat/form_field/behat_form_textarea.php [new file with mode: 0644]
lib/classes/event/assessable_submitted.php
lib/classes/event/assessable_uploaded.php
lib/classes/event/base.php
lib/classes/event/course_module_completion_updated.php
lib/classes/event/course_module_instance_list_viewed.php
lib/classes/event/course_module_viewed.php
lib/classes/log/dummy_manager.php [new file with mode: 0644]
lib/classes/log/manager.php [new file with mode: 0644]
lib/classes/log/reader.php [moved from lib/classes/task/delete_logs_task.php with 51% similarity]
lib/classes/log/sql_internal_reader.php [new file with mode: 0644]
lib/classes/log/sql_select_reader.php [new file with mode: 0644]
lib/classes/plugin_manager.php
lib/completionlib.php
lib/csslib.php
lib/datalib.php
lib/db/tasks.php
lib/deprecatedlib.php
lib/dml/sqlsrv_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/flowplayer/README.txt
lib/flowplayer/README_audio.txt
lib/flowplayer/flowplayer-3.2.12.min.js [deleted file]
lib/flowplayer/flowplayer-3.2.13.js [moved from lib/flowplayer/flowplayer-3.2.12.js with 98% similarity]
lib/flowplayer/flowplayer-3.2.13.min.js [new file with mode: 0644]
lib/flowplayer/flowplayer-3.2.16.swf [deleted file]
lib/flowplayer/flowplayer-3.2.18.swf [new file with mode: 0644]
lib/flowplayer/flowplayer.audio-3.2.10.swf [deleted file]
lib/flowplayer/flowplayer.audio-3.2.11.swf [new file with mode: 0644]
lib/flowplayer/flowplayer.controls-3.2.15.swf [deleted file]
lib/flowplayer/flowplayer.controls-3.2.16.swf [new file with mode: 0644]
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js [new file with mode: 0644]
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js [new file with mode: 0644]
lib/form/yui/dateselector/assets/skins/sam/dateselector.css [deleted file]
lib/form/yui/dateselector/dateselector.js [deleted file]
lib/form/yui/src/dateselector/build.json [new file with mode: 0644]
lib/form/yui/src/dateselector/js/calendar.js [new file with mode: 0644]
lib/form/yui/src/dateselector/js/dateselector.js [new file with mode: 0644]
lib/form/yui/src/dateselector/js/moodlecalendar.js [new file with mode: 0644]
lib/form/yui/src/dateselector/meta/dateselector.json [new file with mode: 0644]
lib/javascript-static.js
lib/moodlelib.php
lib/outputlib.php
lib/phpunit/classes/util.php
lib/questionlib.php
lib/tests/behat/behat_data_generators.php
lib/tests/behat/behat_deprecated.php
lib/tests/behat/behat_forms.php
lib/tests/behat/behat_permissions.php
lib/tests/completionlib_test.php
lib/tests/datalib_update_with_unique_index_test.php [new file with mode: 0644]
lib/tests/event_test.php
lib/tests/fixtures/event_fixtures.php
lib/thirdpartylibs.xml
message/tests/behat/behat_message.php
message/tests/behat/block_users.feature
message/tests/behat/display_history.feature
message/tests/behat/manage_contacts.feature
message/tests/behat/message_participants.feature
message/tests/behat/search_history.feature
mod/assign/externallib.php
mod/assign/tests/behat/allow_another_attempt.feature
mod/assign/tests/behat/comment_inline.feature
mod/assign/tests/behat/edit_previous_feedback.feature
mod/assign/tests/behat/group_submission.feature
mod/assign/tests/behat/online_submissions.feature
mod/assign/tests/behat/prevent_submission_changes.feature
mod/assign/tests/behat/quickgrading.feature
mod/book/index.php
mod/book/view.php
mod/chat/chat_ajax.php
mod/chat/index.php
mod/choice/index.php
mod/choice/tests/behat/behat_mod_choice.php
mod/choice/tests/behat/change_response.feature
mod/choice/tests/behat/publish_results.feature
mod/choice/view.php
mod/data/index.php
mod/data/mod_form.php
mod/data/view.php
mod/feedback/classes/event/course_module_viewed.php
mod/feedback/complete.php
mod/feedback/complete_guest.php
mod/feedback/index.php
mod/feedback/tests/events_test.php
mod/feedback/view.php
mod/folder/index.php
mod/folder/view.php
mod/forum/classes/event/course_module_viewed.php [moved from mod/forum/classes/event/forum_viewed.php with 65% similarity]
mod/forum/index.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/renderer.php
mod/forum/tests/behat/behat_mod_forum.php
mod/forum/tests/behat/completion_condition_number_discussions.feature
mod/forum/tests/behat/discussion_display.feature
mod/forum/tests/behat/edit_post_student.feature
mod/forum/tests/behat/edit_post_teacher.feature
mod/forum/tests/events_test.php
mod/forum/view.php
mod/glossary/lib.php
mod/glossary/tests/behat/behat_mod_glossary.php
mod/glossary/tests/behat/prevent_duplicate_entries.feature
mod/glossary/tests/behat/search_entries.feature
mod/lesson/index.php
mod/lesson/tests/behat/date_availability.feature
mod/lesson/tests/behat/lesson_navigation.feature
mod/lesson/tests/behat/password_protection.feature
mod/lesson/tests/behat/time_limit.feature
mod/lesson/view.php
mod/lti/index.php
mod/lti/view.php
mod/page/index.php
mod/quiz/accessrule/delaybetweenattempts/tests/rule_test.php
mod/quiz/accessrule/ipaddress/tests/rule_test.php
mod/quiz/accessrule/numattempts/tests/rule_test.php
mod/quiz/accessrule/openclosedate/tests/rule_test.php
mod/quiz/accessrule/password/tests/rule_test.php
mod/quiz/accessrule/safebrowser/tests/rule_test.php
mod/quiz/accessrule/securewindow/tests/rule_test.php
mod/quiz/accessrule/timelimit/tests/rule_test.php
mod/quiz/attemptlib.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/backup/moodle2/restore_quiz_stepslib.php
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/edit.php
mod/quiz/editlib.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/override_form.php
mod/quiz/report/grading/report.php
mod/quiz/report/overview/report.php
mod/quiz/report/reportlib.php
mod/quiz/report/responses/report.php
mod/quiz/report/statistics/report.php
mod/quiz/tests/attempt_walkthrough_from_csv_test.php
mod/quiz/tests/attempt_walkthrough_test.php
mod/quiz/tests/behat/add_quiz.feature
mod/quiz/tests/behat/behat_mod_quiz.php
mod/quiz/tests/editlib_test.php
mod/quiz/tests/generator/lib.php
mod/quiz/tests/locallib_test.php
mod/quiz/tests/quizobj_test.php
mod/quiz/upgrade.txt
mod/quiz/version.php
mod/quiz/view.php
mod/resource/index.php
mod/resource/view.php
mod/scorm/index.php
mod/scorm/tests/behat/add_scorm.feature
mod/scorm/view.php
mod/survey/tests/behat/survey_types.feature
mod/url/index.php
mod/url/view.php
mod/wiki/admin.php
mod/wiki/comments.php
mod/wiki/create_form.php
mod/wiki/diff.php
mod/wiki/edit.php
mod/wiki/editcomments.php
mod/wiki/files.php
mod/wiki/filesedit.php
mod/wiki/history.php
mod/wiki/index.php
mod/wiki/lib.php
mod/wiki/locallib.php
mod/wiki/lock.php
mod/wiki/map.php
mod/wiki/overridelocks.php
mod/wiki/prettyview.php
mod/wiki/restoreversion.php
mod/wiki/search.php
mod/wiki/tests/behat/collaborative_individual.feature
mod/wiki/tests/behat/page_history.feature
mod/wiki/tests/behat/preview_page.feature
mod/wiki/tests/behat/wiki_formats.feature
mod/wiki/view.php
mod/wiki/viewversion.php
mod/workshop/classes/event/course_module_instance_list_viewed.php [moved from mod/workshop/classes/event/instances_list_viewed.php with 54% similarity]
mod/workshop/index.php
mod/workshop/lang/en/workshop.php
mod/workshop/submission.php
mod/workshop/tests/events_test.php
mod/workshop/view.php
question/engine/upgrade/upgradelib.php
question/tests/behat/behat_question.php
question/tests/behat/copy_questions.feature
question/tests/behat/edit_questions.feature
question/tests/behat/preview_question.feature
question/tests/behat/question_categories.feature
question/type/calculated/questiontype.php
question/type/calculated/tests/upgradelibnewqe_test.php
question/type/calculatedmulti/tests/upgradelibnewqe_test.php
question/type/calculatedsimple/tests/upgradelibnewqe_test.php
question/type/description/tests/upgradelibnewqe_test.php
question/type/essay/tests/upgradelibnewqe_test.php
question/type/match/tests/upgradelibnewqe_test.php
question/type/multianswer/edit_multianswer_form.php
question/type/multianswer/tests/upgradelibnewqe_test.php
question/type/multichoice/tests/upgradelibnewqe_test.php
question/type/numerical/tests/upgradelibnewqe_test.php
question/type/random/tests/upgradelibnewqe_test.php
question/type/shortanswer/tests/upgradelibnewqe_test.php
question/type/truefalse/tests/upgradelibnewqe_test.php
repository/alfresco/db/upgrade.php [new file with mode: 0644]
repository/alfresco/db/upgradelib.php [new file with mode: 0644]
repository/alfresco/lang/en/repository_alfresco.php
repository/alfresco/lib.php
repository/alfresco/version.php
repository/recent/tests/behat/add_recent.feature
repository/tests/behat/cancel_add_file.feature
repository/tests/behat/create_folders.feature
repository/tests/behat/create_shortcut.feature
repository/tests/behat/overwrite_file.feature
repository/tests/behat/zip_and_unzip.feature
theme/styles.php
theme/styles_debug.php
theme/upgrade.txt
user/tests/behat/edituserpassword.feature

index 20f69b2..ec37e43 100644 (file)
@@ -137,21 +137,6 @@ $temp->add(new admin_setting_configselect('deleteincompleteusers', new lang_stri
                                                                                                                                                                     48 => new lang_string('numdays', '', 2),
                                                                                                                                                                     24 => new lang_string('numdays', '', 1))));
 
-$temp->add(new admin_setting_configcheckbox('logguests', new lang_string('logguests', 'admin'),
-                                            new lang_string('logguests_help', 'admin'), 1));
-$temp->add(new admin_setting_configselect('loglifetime', new lang_string('loglifetime', 'admin'), new lang_string('configloglifetime', 'admin'), 0, array(0 => new lang_string('neverdeletelogs'),
-                                                                                                                                                1000 => new lang_string('numdays', '', 1000),
-                                                                                                                                                365 => new lang_string('numdays', '', 365),
-                                                                                                                                                180 => new lang_string('numdays', '', 180),
-                                                                                                                                                150 => new lang_string('numdays', '', 150),
-                                                                                                                                                120 => new lang_string('numdays', '', 120),
-                                                                                                                                                90 => new lang_string('numdays', '', 90),
-                                                                                                                                                60 => new lang_string('numdays', '', 60),
-                                                                                                                                                35 => new lang_string('numdays', '', 35),
-                                                                                                                                                10 => new lang_string('numdays', '', 10),
-                                                                                                                                                5 => new lang_string('numdays', '', 5),
-                                                                                                                                                2 => new lang_string('numdays', '', 2))));
-
 
 $temp->add(new admin_setting_configcheckbox('disablegradehistory', new lang_string('disablegradehistory', 'grades'),
                                             new lang_string('disablegradehistory_help', 'grades'), 0));
index b5d6096..1b41f65 100644 (file)
@@ -17,7 +17,7 @@ Feature: Display extended course names
   Scenario: Courses list with extended course names
     Given I expand "Site administration" node
     And I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][contains(., 'Appearance')]" "xpath_element"
-    And I check "Display extended course names"
+    And I set the field "Display extended course names" to "1"
     When I press "Save changes"
     And I am on homepage
     Then I should see "C_shortname Course fullname"
index 4b7f6ef..1b7de06 100644 (file)
@@ -29,7 +29,7 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
 
   @javascript
   Scenario: Filter user accounts by role and cohort
-    When I fill the moodle form with:
+    When I set the following fields to these values:
       | courserole_rl | Student |
       | courserole_ct | any category |
       | courserole | C1 |
@@ -38,7 +38,7 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
     And I should see "User Two"
     And I should see "User Three"
     And I should not see "User Four"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | cohort | CH1 |
     And I press "Add filter"
     And I should not see "User One"
@@ -53,14 +53,14 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
 
   @javascript
   Scenario: Filter user accounts by confirm and authentication method
-    When I fill the moodle form with:
+    When I set the following fields to these values:
       | Confirmed | No |
     And I press "Add filter"
     Then I should see "User One"
     And I should not see "User Two"
     And I should not see "User Three"
     And I should see "User Four"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Authentication | manual |
     And I press "Add filter"
     And I should see "User One"
index 5cde0e8..d67085b 100644 (file)
@@ -41,5 +41,5 @@ Feature: Upload users
     And I follow "Maths"
     And I expand "Users" node
     And I follow "Groups"
-    And I select "Section 1 (1)" from "groups"
+    And I set the field "groups" to "Section 1 (1)"
     And the "members" select box should contain "Tom Jones"
index b07e92f..88a1ef7 100644 (file)
@@ -29,6 +29,8 @@ $string['errorcomposer'] = 'Composer dependencies are not installed.';
 $string['errordataroot'] = '$CFG->behat_dataroot is not set or is invalid.';
 $string['errorsetconfig'] = '$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot need to be set in config.php.';
 $string['erroruniqueconfig'] = '$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot values need to be different than $CFG->dataroot, $CFG->prefix, $CFG->wwwroot, $CFG->phpunit_dataroot and $CFG->phpunit_prefix values.';
+$string['fieldvalueargument'] = 'Field value arguments';
+$string['fieldvalueargument_help'] = 'This argument should be completed by a field value, there are many field types, simple ones like checkboxes, selects or textareas or complex ones like date selectors. You can check <a href="http://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps" target="_blank">Field values</a> to see the expected field value depending on the field type you provide.';
 $string['giveninfo'] = 'Given. Processes to set up the environment';
 $string['infoheading'] = 'Info';
 $string['installinfo'] = 'Read {$a} for installation and tests execution info';
index cc5b4ab..0454436 100644 (file)
@@ -84,6 +84,33 @@ class tool_behat_renderer extends plugin_renderer_base {
                 $stepsdefinitions
             );
 
+            $stepsdefinitions = preg_replace_callback('/(FIELD_VALUE_STRING)/',
+                function ($matches) {
+                    global $CFG;
+
+                    // Creating a link to a popup with the help.
+                    $url = new moodle_url(
+                        '/help.php',
+                        array(
+                            'component' => 'tool_behat',
+                            'identifier' => 'fieldvalueargument',
+                            'lang' => current_language()
+                        )
+                    );
+
+                    // Note: this title is displayed only if JS is disabled,
+                    // otherwise the link will have the new ajax tooltip.
+                    $title = get_string('fieldvalueargument', 'tool_behat');
+                    $title = get_string('helpprefix2', '', trim($title, ". \t"));
+
+                    $attributes = array('href' => $url, 'title' => $title,
+                        'aria-haspopup' => 'true', 'target' => '_blank');
+
+                    $output = html_writer::tag('a', 'FIELD_VALUE_STRING', $attributes);
+                    return html_writer::tag('span', $output, array('class' => 'helptooltip'));
+                },
+                $stepsdefinitions
+            );
         }
 
         // Steps definitions.
index 83f0320..4f482dc 100644 (file)
@@ -11,7 +11,7 @@ Feature: Page contents assertions
     And I expand "Users" node
     And I follow "Groups"
     And I press "Create group"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Group name | I'm the name |
       | Group description | I'm the description |
     And I press "Save changes"
index b460b1a..59e5339 100644 (file)
@@ -89,11 +89,11 @@ Feature: Set up contextual data for tests
     And I follow "Course 1"
     And I expand "Users" node
     And I follow "Permissions"
-    And I select "Student (1)" from "Advanced role override"
-    Then the "mod/forum:editanypost" field should match "1" value
+    And I set the field "Advanced role override" to "Student (1)"
+    Then "mod/forum:editanypost" capability has "Allow" permission
     And I press "Cancel"
-    And I select "Teacher (1)" from "Advanced role override"
-    And the "mod/forum:replynews" field should match "-1" value
+    And I set the field "Advanced role override" to "Teacher (1)"
+    And "mod/forum:replynews" capability has "Prevent" permission
     And I press "Cancel"
 
   Scenario: Add course enrolments
@@ -249,7 +249,7 @@ Feature: Set up contextual data for tests
     And I follow "Groups"
     Then the "groups" select box should contain "Group 1 (1)"
     And the "groups" select box should contain "Group 2 (1)"
-    And I select "Group 1 (1)" from "groups"
+    And I set the field "groups" to "Group 1 (1)"
     And the "members" select box should contain "Student 1"
-    And I select "Group 2 (1)" from "groups"
+    And I set the field "groups" to "Group 2 (1)"
     And the "members" select box should contain "Student 2"
index b16b0e0..c2111ec 100644 (file)
@@ -25,10 +25,10 @@ Feature: Edit capabilities
       | moodle/grade:managesharedforms | Prevent |
       | moodle/course:request | Prohibit |
     When I follow "Edit Teacher role"
-    Then the "block/mnet_hosts:myaddinstance" field should match "1" value
-    And the "moodle/community:add" field should match "0" value
-    And the "moodle/grade:managesharedforms" field should match "-1" value
-    And the "moodle/course:request" field should match "-1000" value
+    Then "block/mnet_hosts:myaddinstance" capability has "Allow" permission
+    And "moodle/community:add" capability has "Not set" permission
+    And "moodle/grade:managesharedforms" capability has "Prevent" permission
+    And "moodle/course:request" capability has "Prohibit" permission
 
   @javascript
   Scenario: Course capabilities overrides
@@ -40,10 +40,10 @@ Feature: Edit capabilities
       | mod/forum:deleteanypost | Prohibit |
       | mod/forum:editanypost | Prevent |
       | mod/forum:addquestion | Allow |
-    When I select "Student (3)" from "Advanced role override"
-    Then the "mod/forum:deleteanypost" field should match "-1000" value
-    And the "mod/forum:editanypost" field should match "-1" value
-    And the "mod/forum:addquestion" field should match "1" value
+    When I set the field "Advanced role override" to "Student (3)"
+    Then "mod/forum:deleteanypost" capability has "Prohibit" permission
+    And "mod/forum:editanypost" capability has "Prevent" permission
+    And "mod/forum:addquestion" capability has "Allow" permission
 
   @javascript
   Scenario: Module capabilities overrides
@@ -59,7 +59,7 @@ Feature: Edit capabilities
       | mod/forum:deleteanypost | Prohibit |
       | mod/forum:editanypost | Prevent |
       | mod/forum:addquestion | Allow |
-    When I select "Student (3)" from "Advanced role override"
-    Then the "mod/forum:deleteanypost" field should match "-1000" value
-    And the "mod/forum:editanypost" field should match "-1" value
-    And the "mod/forum:addquestion" field should match "1" value
+    When I set the field "Advanced role override" to "Student (3)"
+    Then "mod/forum:deleteanypost" capability has "Prohibit" permission
+    And "mod/forum:editanypost" capability has "Prevent" permission
+    And "mod/forum:addquestion" capability has "Allow" permission
diff --git a/admin/tool/behat/tests/behat/get_and_set_fields.feature b/admin/tool/behat/tests/behat/get_and_set_fields.feature
new file mode 100644 (file)
index 0000000..3857653
--- /dev/null
@@ -0,0 +1,173 @@
+@tool_behat
+Feature: Verify that all form fields values can be get and set
+  In order to use behat steps definitions
+  As a test writer
+  I need to verify it all works in real moodle forms
+
+  Background:
+    Given the following "courses" exists:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "users" exists:
+      | username | email | firstname | lastname |
+      | student1 | s1@asd.com | Student | 1 |
+      | student2 | s2@asd.com | Student | 2 |
+      | student3 | s3@asd.com | Student | 3 |
+    And the following "course enrolments" exists:
+      | user | course | role |
+      | student1 | C1 | student |
+      | student2 | C1 | student |
+      | student3 | C1 | student |
+      | admin | C1 | editingteacher |
+    And the following "groups" exists:
+      | name | description | course | idnumber |
+      | Group 1 | G1 description | C1 | G1 |
+      | Group 2 | G1 description | C1 | G2 |
+    And the following "group members" exists:
+      | user | group |
+      | student1 | G1 |
+      | student2 | G1 |
+      | student2 | G2 |
+      | student3 | G2 |
+    And the following "activities" exists:
+      | activity | course | idnumber | name | intro | firstpagetitle | wikimode | visible |
+      | wiki | C1 | wiki1 | Test this one | Test this one | Test this one | collaborative | 0 |
+    And I log in as "admin"
+    And I expand "Site administration" node
+    And I expand "Appearance" node
+    And I follow "Manage tags"
+    # Select (multi-select) - We will check "I set the field...".
+    And I set the field "otagsadd" to "OT1, OT2, OT3, OT4, OT5"
+    And I press "Add official tags"
+    And I am on homepage
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I follow "Test this one"
+    And I press "Create page"
+    # Select (multi-select) - Checking "the select box should contain".
+    And the "tags[officialtags][]" select box should contain "OT1"
+    And the "tags[officialtags][]" select box should contain "OT2"
+    And the "tags[officialtags][]" select box should contain "OT3"
+    And the "tags[officialtags][]" select box should contain "OT4"
+    And the "tags[officialtags][]" select box should contain "OT5"
+    And the "tags[officialtags][]" select box should contain "OT1, OT2, OT3, OT4, OT5"
+    And the "tags[officialtags][]" select box should contain "OT5, OT4, OT3, OT2, OT1"
+    And the "tags[officialtags][]" select box should not contain "OT6"
+    And the "tags[officialtags][]" select box should not contain "OT7"
+    And the "tags[officialtags][]" select box should not contain "OT6, OT7"
+    # Text (textarea & editor) & Select (multi-select) - Checking "I set the following fields to these values".
+    When I set the following fields to these values:
+      | HTML format | Student page contents to be tagged |
+      | tags[officialtags][] | OT1, OT3, OT5 |
+    And I press "Save"
+    Then I should see "Student page contents to be tagged" in the "region-main" "region"
+    And I should see "OT1" in the ".wiki-tags" "css_element"
+    And I should see "OT3" in the ".wiki-tags" "css_element"
+    And I should see "OT5" in the ".wiki-tags" "css_element"
+    And I should not see "OT2" in the ".wiki-tags" "css_element"
+    And I should not see "OT4" in the ".wiki-tags" "css_element"
+    And I follow "Edit"
+    # Select (multi-select) - Checking "I set the field".
+    And I set the field "tags[officialtags][]" to "OT2, OT4"
+    And I press "Save"
+    And I should see "OT2" in the ".wiki-tags" "css_element"
+    And I should see "OT4" in the ".wiki-tags" "css_element"
+    And I should not see "OT1" in the ".wiki-tags" "css_element"
+    And I should not see "OT3" in the ".wiki-tags" "css_element"
+    And I should not see "OT5" in the ".wiki-tags" "css_element"
+    And I follow "Edit"
+    # Select (multi-select) - Checking "the field matches value" and "the field does not match value".
+    And the field "tags[officialtags][]" matches value "OT2, OT4"
+    And the field "tags[officialtags][]" does not match value "OT4"
+    And the field "tags[officialtags][]" does not match value "OT2"
+    And the field "tags[officialtags][]" does not match value "OT1, OT3, OT5"
+    And I press "Cancel"
+    And I follow "Edit settings"
+    And I expand all fieldsets
+    # Checkbox - Checking "I set the field".
+    And I set the field "Display description on course page" to "1"
+    # Checkbox - Checking "I set the following fields to these values:".
+    And I set the following fields to these values:
+      | Force format | 1 |
+    # Checkbox - Checking "the field matches value" and "the field does not match value".
+    And the field "Display description on course page" matches value "1"
+    And the field "Display description on course page" does not match value ""
+    And I press "Save and return to course"
+    And I should see "Test this one"
+    And I follow "Test this one"
+    And I follow "Edit settings"
+    # Checkbox - Checking "the field matches value" and "the following fields match these values".
+    And the following fields match these values:
+      | Display description on course page | 1 |
+      | Default format | HTML |
+      | Wiki name | Test this one |
+    And the field "Force format" matches value "1"
+    # Select (simple) - Checking "I set the following fields to these values:".
+    And I set the following fields to these values:
+      | Default format | NWiki |
+      | Display description on course page | |
+    # Checkbox - Checking "I set the field" to uncheck.
+    And I set the field "Force format" to ""
+    # Select (simple) - Checking "I set the field".
+    And I set the field "Group mode" to "Separate groups"
+    And I press "Save and display"
+    And I follow "Edit settings"
+    And the following fields match these values:
+      | Default format | NWiki |
+      | Group mode | Separate groups |
+      | Display description on course page | |
+      | Force format | |
+    # All fields - Checking "the following fields do not match these values".
+    And the following fields do not match these values:
+      | Wiki name | Test this one baby |
+      | Default format | HTML |
+      | Force format | 1 |
+    And I press "Cancel"
+    And I follow "Course 1"
+    # Radio - Checking "I set the field" and "the field matches value".
+    And I add a "Choice" to section "1" and I fill the form with:
+      | Choice name | Test choice name |
+      | Description | Test choice description |
+      | Allow choice to be updated | Yes |
+      | Option 1 | one |
+      | Option 2 | two |
+      | Option 3 | three |
+    And I follow "Test choice name"
+    And I set the field "one" to "1"
+    And I press "Save my choice"
+    And the field "one" matches value "1"
+    And the field "two" matches value ""
+
+  Scenario: with JS disabled all form fields getters and setters works as expected
+
+  @javascript
+  Scenario: with JS enabled all form fields getters and setters works as expected
+    Then I follow "Course 1"
+    And I expand "Users" node
+    And I follow "Groups"
+    # Select (multi-select & AJAX) - Checking "I set the field" and "select box should contain".
+    And I set the field "groups" to "Group 2"
+    And the "members" select box should contain "Student 2"
+    And the "members" select box should contain "Student 3"
+    And the "members" select box should not contain "Student 1"
+    And I set the field "groups" to "Group 1"
+    And the "members" select box should contain "Student 1"
+    And the "members" select box should contain "Student 2"
+    And the "members" select box should not contain "Student 3"
+    # Checkbox (AJAX) - Checking "I set the field" and "I set the following fields to these values".
+    And I follow "Course 1"
+    And I add a "Lesson" to section "1"
+    And I set the following fields to these values:
+      | Name | Test lesson |
+      | available[enabled] | 1 |
+    And I set the field "deadline[enabled]" to "1"
+    # Checkbox (AJAX) - Checking "the field matches value" before saving.
+    And the field "available[enabled]" matches value "1"
+    And the "available[day]" "field" should be enabled
+    And the field "deadline[enabled]" matches value "1"
+    And I press "Save and display"
+    And I follow "Edit settings"
+    And the field "available[enabled]" matches value "1"
+    And the "available[day]" "field" should be enabled
+    And the field "deadline[enabled]" matches value "1"
+    And I press "Cancel"
index 3fc9ce8..8061f4e 100644 (file)
@@ -18,14 +18,14 @@ Feature: List the system steps definitions
 
   @javascript
   Scenario: Filtering by type
-    Given I select "Then. Checkings to ensure the outcomes are the expected ones" from "Type"
+    Given I set the field "Type" to "Then. Checkings to ensure the outcomes are the expected ones"
     When I press "Filter"
     Then I should see "Checks, that page contains specified text."
     And I should not see "Opens Moodle homepage."
 
   @javascript
   Scenario: Filtering by keyword
-    Given I fill in "Contains" with "homepage"
+    Given I set the field "Contains" to "homepage"
     When I press "Filter"
     Then I should see "Opens Moodle homepage."
 
index 664bbd6..ce44fad 100644 (file)
@@ -9,14 +9,14 @@ Feature: Forms manipulation
     Given I log in as "admin"
     And I follow "Admin User"
     And I follow "Edit profile"
-    When I fill in "First name" with "Field value"
-    And I select "Plain text area" from "Text editor"
-    And I check "Unmask"
-    Then the "First name" field should match "Field value" value
+    When I set the field "First name" to "Field value"
+    And I set the field "Text editor" to "Plain text area"
+    And I set the field "Unmask" to "1"
+    Then the field "First name" matches value "Field value"
     And the "Text editor" select box should contain "Plain text area"
-    And the "Unmask" checkbox should be checked
-    And I uncheck "Unmask"
-    And the "Unmask" checkbox should not be checked
+    And the field "Unmask" matches value "1"
+    And I set the field "Unmask" to ""
+    And the field "Unmask" matches value ""
     And I press "Update profile"
 
   @javascript
index 8ced5fd..c67d6cd 100644 (file)
@@ -14,47 +14,47 @@ Feature: Transform steps arguments
     And I follow "Edit profile"
 
   Scenario: Use nasty strings on steps arguments
-    When I fill in "Surname" with "$NASTYSTRING1"
-    And I fill in "Description" with "$NASTYSTRING2"
-    And I fill in "City/town" with "$NASTYSTRING3"
+    When I set the field "Surname" to "$NASTYSTRING1"
+    And I set the field "Description" to "$NASTYSTRING2"
+    And I set the field "City/town" to "$NASTYSTRING3"
     And I press "Update profile"
     And I follow "Edit profile"
     Then I should not see "NASTYSTRING"
-    And the "Surname" field should match "$NASTYSTRING1" value
-    And the "City/town" field should match "$NASTYSTRING3" value
+    And the field "Surname" matches value "$NASTYSTRING1"
+    And the field "City/town" matches value "$NASTYSTRING3"
 
   Scenario: Use nasty strings on table nodes
-    When I fill the moodle form with:
+    When I set the following fields to these values:
       | Surname | $NASTYSTRING1 |
       | Description | $NASTYSTRING2 |
       | City/town | $NASTYSTRING3 |
     And I press "Update profile"
     And I follow "Edit profile"
     Then I should not see "NASTYSTRING"
-    And the "Surname" field should match "$NASTYSTRING1" value
-    And the "City/town" field should match "$NASTYSTRING3" value
+    And the field "Surname" matches value "$NASTYSTRING1"
+    And the field "City/town" matches value "$NASTYSTRING3"
 
   Scenario: Use double quotes
-    When I fill the moodle form with:
+    When I set the following fields to these values:
       | First name | va"lue1 |
       | Description | va\"lue2 |
-    And I fill in "City/town" with "va\"lue3"
+    And I set the field "City/town" to "va\"lue3"
     And I press "Update profile"
     And I follow "Edit profile"
     Then I should not see "NASTYSTRING"
-    And the "First name" field should match "va\"lue1" value
-    And the "Description" field should match "va\\"lue2" value
-    And the "City/town" field should match "va\"lue3" value
+    And the field "First name" matches value "va\"lue1"
+    And the field "Description" matches value "va\\"lue2"
+    And the field "City/town" matches value "va\"lue3"
 
   @javascript
   Scenario: Nasty strings with other contents
-    When I fill in "First name" with "My Firstname $NASTYSTRING1"
-    And I fill the moodle form with:
+    When I set the field "First name" to "My Firstname $NASTYSTRING1"
+    And I set the following fields to these values:
       | Surname | My Surname $NASTYSTRING2 |
     And I press "Update profile"
     And I follow "Edit profile"
     Then I should not see "NASTYSTRING"
     And I should see "My Firstname"
     And I should see "My Surname"
-    And the "First name" field should match "My Firstname $NASTYSTRING1" value
-    And the "Surname" field should match "My Surname $NASTYSTRING2" value
+    And the field "First name" matches value "My Firstname $NASTYSTRING1"
+    And the field "Surname" matches value "My Surname $NASTYSTRING2"
index e677b90..525aa94 100644 (file)
@@ -1,6 +1,8 @@
-This files describes API changes in the assign code.
+This files describes API changes in the tool_behat code.
 
 === 2.7 ===
-
+* Constants behat_base::cap_allow, behat_base::cap_prevent and
+  behat_base::cap_prohibit have been removed in favour of the
+  lang/en/role.php language strings 'allow', 'prevent' and 'prohibit'.
 * @_only_local tag used in .feature files replaced by @_file_upload tag
 * @_alerts tag used in .feature files replaced by @_alert tag
diff --git a/admin/tool/log/classes/helper/buffered_writer.php b/admin/tool/log/classes/helper/buffered_writer.php
new file mode 100644 (file)
index 0000000..d341452
--- /dev/null
@@ -0,0 +1,106 @@
+<?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/>.
+
+/**
+ * Helper trait buffered_writer
+ *
+ * @package    tool_log
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\helper;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Helper trait buffered_writer. Adds buffer support for the store.
+ * \tool_log\helper\store must be included before using this trait.
+ *
+ * @package    tool_log
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+trait buffered_writer {
+
+    /** @var array $buffer buffer of events. */
+    protected $buffer = array();
+
+    /** @var array $buffer buffer size of events. */
+    protected $buffersize;
+
+    /** @var int $count Counter. */
+    protected $count = 0;
+
+    /**
+     * Write event in the store with buffering. Method insert_events() must be
+     * defined.
+     *
+     * @param \core\event\base $event
+     *
+     * @return void
+     */
+    public function write(\core\event\base $event) {
+        $this->buffer[] = $event;
+        $this->count++;
+
+        if (!isset($this->buffersize)) {
+            $this->buffersize = $this->get_config('buffersize', 50);
+        }
+
+        if ($this->count >= $this->buffersize) {
+            $this->flush();
+        }
+    }
+
+    /**
+     * Flush event buffer.
+     */
+    public function flush() {
+        if ($this->count == 0) {
+            return;
+        }
+        $events = $this->buffer;
+        $this->count = 0;
+        $this->buffer = array();
+        $this->insert_events($events);
+    }
+
+    /**
+     * Bulk write a given array of events to the backend. Stores must implement this.
+     *
+     * @param array $events An array of events to write.
+     */
+    abstract protected function insert_events($events);
+
+    /**
+     * Get a config value for the store.
+     *
+     * @param string $name Config name
+     * @param mixed $default default value
+     *
+     * @return mixed config value if set, else the default value.
+     */
+    abstract protected function get_config($name, $default = null);
+
+    /**
+     * Push any remaining events to the database. Insert_events() must be
+     * defined. override in stores if the store doesn't support buffering.
+     *
+     */
+    public function dispose() {
+        $this->flush();
+    }
+}
diff --git a/admin/tool/log/classes/helper/reader.php b/admin/tool/log/classes/helper/reader.php
new file mode 100644 (file)
index 0000000..036f874
--- /dev/null
@@ -0,0 +1,75 @@
+<?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/>.
+
+/**
+ * Reader helper trait.
+ *
+ * @package    tool_log
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\helper;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Reader helper trait.
+ * \tool_log\helper\store must be included before using this trait.
+ *
+ * @package    tool_log
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ *
+ * @property string $component Frankenstyle plugin name initialised in store trait.
+ * @property string $store short plugin name initialised in store trait.
+ */
+trait reader {
+    /**
+     * Default get name api.
+     *
+     * @return string name of the store.
+     */
+    public function get_name() {
+        if (get_string_manager()->string_exists('pluginname', $this->component)) {
+            return get_string('pluginname', $this->component);
+        }
+        return $this->store;
+    }
+
+    /**
+     * Default get description method.
+     *
+     * @return string description of the store.
+     */
+    public function get_description() {
+        if (get_string_manager()->string_exists('pluginname_desc', $this->component)) {
+            return get_string('pluginname_desc', $this->component);
+        }
+        return $this->store;
+    }
+
+    /**
+     * If the current user can access current store or not.
+     *
+     * @param \context $context
+     *
+     * @return bool
+     */
+    public function can_access(\context $context) {
+        return has_capability('logstore/' . $this->store . ':read', $context);
+    }
+}
diff --git a/admin/tool/log/classes/helper/store.php b/admin/tool/log/classes/helper/store.php
new file mode 100644 (file)
index 0000000..f3bf4bb
--- /dev/null
@@ -0,0 +1,79 @@
+<?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/>.
+
+/**
+ * Helper trait store.
+ *
+ * @package    tool_log
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\helper;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Helper trait store. Adds some helper methods for stores.
+ *
+ * @package    tool_log
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+trait store {
+
+    /** @var \tool_log\log\manager $manager manager instance. */
+    protected $manager;
+
+    /** @var string $component Frankenstyle store name. */
+    protected $component;
+
+    /** @var string $store name of the store. */
+    protected $store;
+
+
+    /**
+     * Setup store specific variables.
+     *
+     * @param \tool_log\log\manager $manager manager instance.
+     */
+    protected function helper_setup(\tool_log\log\manager $manager) {
+        $this->manager = $manager;
+        $called = get_called_class();
+        $parts = explode('\\', $called);
+        if (!isset($parts[0]) || strpos($parts[0], 'logstore_') !== 0) {
+            throw new \coding_exception("Store $called doesn't define classes in correct namespaces.");
+        }
+        $this->component = $parts[0];
+        $this->store = str_replace('logstore_', '', $this->store);
+    }
+
+    /**
+     * Api to get plugin config
+     *
+     * @param string $name name of the config.
+     * @param null|mixed $default default value to return.
+     *
+     * @return mixed|null return config value.
+     */
+    protected function get_config($name, $default = null) {
+        $value = get_config($this->component, $name);
+        if ($value !== false) {
+            return $value;
+        }
+        return $default;
+    }
+
+}
diff --git a/admin/tool/log/classes/log/manager.php b/admin/tool/log/classes/log/manager.php
new file mode 100644 (file)
index 0000000..894fe94
--- /dev/null
@@ -0,0 +1,168 @@
+<?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/>.
+
+/**
+ * Log store manager.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\log;
+
+defined('MOODLE_INTERNAL') || die();
+
+class manager implements \core\log\manager {
+    /** @var \core\log\reader[] $readers list of initialised log readers */
+    protected $readers;
+
+    /** @var \tool_log\log\writer[] $writers list of initialised log writers */
+    protected $writers;
+
+    /** @var \tool_log\log\store[] $stores list of all enabled stores */
+    protected $stores;
+
+    /**
+     * Delayed initialisation of singleton.
+     */
+    protected function init() {
+        if (isset($this->stores)) {
+            // Do not bother checking readers and writers here
+            // because everything is init here.
+            return;
+        }
+        $this->stores = array();
+        $this->readers = array();
+        $this->writers = array();
+
+        // Register shutdown handler - this may be useful for buffering, file handle closing, etc.
+        \core_shutdown_manager::register_function(array($this, 'dispose'));
+
+        $plugins = get_config('tool_log', 'enabled_stores');
+        if (empty($plugins)) {
+            return;
+        }
+
+        $plugins = explode(',', $plugins);
+        foreach ($plugins as $plugin) {
+            $classname = "\\$plugin\\log\\store";
+            if (class_exists($classname)) {
+                $store = new $classname($this);
+                $this->stores[$plugin] = $store;
+                if ($store instanceof \tool_log\log\writer) {
+                    $this->writers[$plugin] = $store;
+                }
+                if ($store instanceof \core\log\reader) {
+                    $this->readers[$plugin] = $store;
+                }
+            }
+        }
+    }
+
+    /**
+     * Called from the observer only.
+     *
+     * @param \core\event\base $event
+     */
+    public function process(\core\event\base $event) {
+        $this->init();
+        foreach ($this->writers as $plugin => $writer) {
+            try {
+                $writer->write($event, $this);
+            } catch (\Exception $e) {
+                debugging('Exception detected when logging event ' . $event->eventname . ' in ' . $plugin . ': ' .
+                    $e->getMessage(), DEBUG_NORMAL, $e->getTrace());
+            }
+        }
+    }
+
+    /**
+     * Returns list of available log readers.
+     *
+     * This way the reports find out available sources of data.
+     *
+     * @param string $interface Returned stores must implement this interface.
+     *
+     * @return \core\log\reader[] list of available log data readers
+     */
+    public function get_readers($interface = null) {
+        $this->init();
+        $return = array();
+        foreach ($this->readers as $plugin => $reader) {
+            if (empty($interface) || ($reader instanceof $interface)) {
+                $return[$plugin] = $reader;
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Intended for store management, do not use from reports.
+     *
+     * @return store[] Returns list of available store plugins.
+     */
+    public static function get_store_plugins() {
+        return \core_component::get_plugin_list_with_class('logstore', 'log\store');
+    }
+
+    /**
+     * Usually called automatically from shutdown manager,
+     * this allows us to implement buffering of write operations.
+     */
+    public function dispose() {
+        if ($this->stores) {
+            foreach ($this->stores as $store) {
+                $store->dispose();
+            }
+        }
+        $this->stores = null;
+        $this->readers = null;
+        $this->writers = null;
+    }
+
+    /**
+     * Execute cron actions.
+     */
+    public function cron() {
+        $this->init();
+        foreach ($this->stores as $store) {
+            $store->cron();
+        }
+    }
+
+    /**
+     * Legacy add_to_log() redirection.
+     *
+     * To be used only from deprecated add_to_log() function and event trigger() method.
+     *
+     * NOTE: this is hardcoded to legacy log store plugin, hopefully we can get rid of it soon.
+     *
+     * @param int $courseid The course id
+     * @param string $module The module name  e.g. forum, journal, resource, course, user etc
+     * @param string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify
+     * @param string $url The file and parameters used to see the results of the action
+     * @param string $info Additional description information
+     * @param int $cm The course_module->id if there is one
+     * @param int|\stdClass $user If log regards $user other than $USER
+     */
+    public function legacy_add_to_log($courseid, $module, $action, $url = '', $info = '', $cm = 0, $user = 0) {
+        $this->init();
+        if (isset($this->stores['logstore_legacy'])) {
+            $this->stores['logstore_legacy']->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user);
+        }
+    }
+}
diff --git a/admin/tool/log/classes/log/observer.php b/admin/tool/log/classes/log/observer.php
new file mode 100644 (file)
index 0000000..0728937
--- /dev/null
@@ -0,0 +1,43 @@
+<?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/>.
+
+/**
+ * Event observer.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\log;
+
+defined('MOODLE_INTERNAL') || die();
+
+class observer {
+    /**
+     * Redirect all events to this log manager, but only if this
+     * log manager is actually used.
+     *
+     * @param \core\event\base $event
+     */
+    public static function store(\core\event\base $event) {
+        $logmanager = get_log_manager();
+        if (get_class($logmanager) === 'tool_log\log\manager') {
+            /** @var \tool_log\log\manager $logmanager */
+            $logmanager->process($event);
+        }
+    }
+}
diff --git a/admin/tool/log/classes/log/store.php b/admin/tool/log/classes/log/store.php
new file mode 100644 (file)
index 0000000..1e40265
--- /dev/null
@@ -0,0 +1,49 @@
+<?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/>.
+
+/**
+ * Log store interface.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\log;
+
+defined('MOODLE_INTERNAL') || die();
+
+interface store {
+    /**
+     * Create new instance of store,
+     * the calling code must make sure only one instance exists.
+     *
+     * @param manager $manager
+     */
+    public function __construct(\tool_log\log\manager $manager);
+
+    /**
+     * Notify store no more events are going to be written/read from it.
+     * @return void
+     */
+    public function dispose();
+
+    /**
+     * Execute cron actions.
+     * @return void
+     */
+    public function cron();
+}
diff --git a/admin/tool/log/classes/log/writer.php b/admin/tool/log/classes/log/writer.php
new file mode 100644 (file)
index 0000000..138dcc4
--- /dev/null
@@ -0,0 +1,37 @@
+<?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/>.
+
+/**
+ * Log store writer interface.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\log;
+
+defined('MOODLE_INTERNAL') || die();
+
+interface writer extends store {
+    /**
+     * Write one event to the store.
+     *
+     * @param \core\event\base $event
+     * @return void
+     */
+    public function write(\core\event\base $event);
+}
diff --git a/admin/tool/log/classes/plugininfo/logstore.php b/admin/tool/log/classes/plugininfo/logstore.php
new file mode 100644 (file)
index 0000000..d7e19db
--- /dev/null
@@ -0,0 +1,89 @@
+<?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/>.
+
+/**
+ * Subplugin info class.
+ *
+ * @package   tool_log
+ * @copyright 2013 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_log\plugininfo;
+
+use core\plugininfo\base, moodle_url, part_of_admin_tree, admin_settingpage;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Plugin info class for logging store plugins.
+ */
+class logstore extends base {
+
+    public function is_enabled() {
+        $enabled = get_config('tool_log', 'enabled_stores');
+        if (!$enabled) {
+            return false;
+        }
+
+        $enabled = array_flip(explode(',', $enabled));
+        return isset($enabled['logstore_' . $this->name]);
+    }
+
+    public function get_settings_section_name() {
+        return 'logsetting' . $this->name;
+    }
+
+    public function load_settings(part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
+        global $CFG, $USER, $DB, $OUTPUT, $PAGE; // In case settings.php wants to refer to them.
+        $ADMIN = $adminroot; // May be used in settings.php.
+        $section = $this->get_settings_section_name();
+
+        if (!$this->is_installed_and_upgraded()) {
+            return;
+        }
+
+        if (!$hassiteconfig or !file_exists($this->full_path('settings.php'))) {
+            return;
+        }
+
+        $settings = new admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false);
+        include($this->full_path('settings.php'));
+
+        if ($settings) {
+            $ADMIN->add($parentnodename, $settings);
+        }
+    }
+
+    public static function get_manage_url() {
+        return new moodle_url('/admin/settings.php', array('section' => 'managelogging'));
+    }
+
+    public function is_uninstall_allowed() {
+        return true;
+    }
+
+    public function uninstall_cleanup() {
+        $enabled = get_config('tool_log', 'enabled_stores');
+        if ($enabled) {
+            $enabled = array_flip(explode(',', $enabled));
+            unset($enabled['logstore_' . $this->name]);
+            $enabled = array_flip($enabled);
+            set_config('enabled_stores', implode(',', $enabled), 'tool_log');
+        }
+
+        parent::uninstall_cleanup();
+    }
+}
diff --git a/admin/tool/log/classes/setting_managestores.php b/admin/tool/log/classes/setting_managestores.php
new file mode 100644 (file)
index 0000000..bb82a82
--- /dev/null
@@ -0,0 +1,233 @@
+<?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/>.
+
+/**
+ * Store management setting.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/adminlib.php");
+
+class tool_log_setting_managestores extends admin_setting {
+    /**
+     * Calls parent::__construct with specific arguments
+     */
+    public function __construct() {
+        $this->nosave = true;
+        parent::__construct('tool_log_manageui', get_string('managelogging', 'tool_log'), '', '');
+    }
+
+    /**
+     * Always returns true, does nothing.
+     *
+     * @return true
+     */
+    public function get_setting() {
+        return true;
+    }
+
+    /**
+     * Always returns true, does nothing.
+     *
+     * @return true
+     */
+    public function get_defaultsetting() {
+        return true;
+    }
+
+    /**
+     * Always returns '', does not write anything.
+     *
+     * @param mixed $data ignored
+     * @return string Always returns ''
+     */
+    public function write_setting($data) {
+        // Do not write any setting.
+        return '';
+    }
+
+    /**
+     * Checks if $query is one of the available log plugins.
+     *
+     * @param string $query The string to search for
+     * @return bool Returns true if found, false if not
+     */
+    public function is_related($query) {
+        if (parent::is_related($query)) {
+            return true;
+        }
+
+        $query = core_text::strtolower($query);
+        $plugins = \tool_log\log\manager::get_store_plugins();
+        foreach ($plugins as $plugin => $fulldir) {
+            if (strpos(core_text::strtolower($plugin), $query) !== false) {
+                return true;
+            }
+            $localised = get_string('pluginname', $plugin);
+            if (strpos(core_text::strtolower($localised), $query) !== false) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Builds the XHTML to display the control.
+     *
+     * @param string $data Unused
+     * @param string $query
+     * @return string
+     */
+    public function output_html($data, $query = '') {
+        global $OUTPUT, $PAGE;
+
+        // Display strings.
+        $strup = get_string('up');
+        $strdown = get_string('down');
+        $strsettings = get_string('settings');
+        $strenable = get_string('enable');
+        $strdisable = get_string('disable');
+        $struninstall = get_string('uninstallplugin', 'core_admin');
+        $strversion = get_string('version');
+
+        $pluginmanager = core_plugin_manager::instance();
+
+        $available = \tool_log\log\manager::get_store_plugins();
+        $enabled = get_config('tool_log', 'enabled_stores');
+        if (!$enabled) {
+            $enabled = array();
+        } else {
+            $enabled = array_flip(explode(',', $enabled));
+        }
+
+        $allstores = array();
+        foreach ($enabled as $key => $store) {
+            $allstores[$key] = true;
+            $enabled[$key] = true;
+        }
+        foreach ($available as $key => $store) {
+            $allstores[$key] = true;
+            $available[$key] = true;
+        }
+
+        $return = $OUTPUT->heading(get_string('actlogshdr', 'tool_log'), 3, 'main', true);
+        $return .= $OUTPUT->box_start('generalbox loggingui');
+
+        $table = new html_table();
+        $table->head = array(get_string('name'), $strversion, $strenable, $strup . '/' . $strdown, $strsettings, $struninstall);
+        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
+        $table->id = 'logstoreplugins';
+        $table->attributes['class'] = 'admintable generaltable';
+        $table->data = array();
+
+        // Iterate through store plugins and add to the display table.
+        $updowncount = 1;
+        $storecount = count($enabled);
+        $url = new moodle_url('/admin/tool/log/stores.php', array('sesskey' => sesskey()));
+        $printed = array();
+        foreach ($allstores as $store => $unused) {
+            $plugininfo = $pluginmanager->get_plugin_info($store);
+            $version = get_config($store, 'version');
+            if ($version === false) {
+                $version = '';
+            }
+
+            if (get_string_manager()->string_exists('pluginname', $store)) {
+                $name = get_string('pluginname', $store);
+            } else {
+                $name = $store;
+            }
+
+            // Hide/show links.
+            if (isset($enabled[$store])) {
+                $aurl = new moodle_url($url, array('action' => 'disable', 'store' => $store));
+                $hideshow = "<a href=\"$aurl\">";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
+                $isenabled = true;
+                $displayname = "<span>$name</span>";
+            } else {
+                if (isset($available[$store])) {
+                    $aurl = new moodle_url($url, array('action' => 'enable', 'store' => $store));
+                    $hideshow = "<a href=\"$aurl\">";
+                    $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
+                    $isenabled = false;
+                    $displayname = "<span class=\"dimmed_text\">$name</span>";
+                } else {
+                    $hideshow = '';
+                    $isenabled = false;
+                    $displayname = '<span class="notifyproblem">' . $name . '</span>';
+                }
+            }
+            if ($PAGE->theme->resolve_image_location('icon', $store, false)) {
+                $icon = $OUTPUT->pix_icon('icon', '', $store, array('class' => 'icon pluginicon'));
+            } else {
+                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
+            }
+
+            // Up/down link (only if store is enabled).
+            $updown = '';
+            if ($isenabled) {
+                if ($updowncount > 1) {
+                    $aurl = new moodle_url($url, array('action' => 'up', 'store' => $store));
+                    $updown .= "<a href=\"$aurl\">";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" class=\"iconsmall\" /></a>&nbsp;";
+                } else {
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
+                }
+                if ($updowncount < $storecount) {
+                    $aurl = new moodle_url($url, array('action' => 'down', 'store' => $store));
+                    $updown .= "<a href=\"$aurl\">";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" class=\"iconsmall\" /></a>";
+                } else {
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
+                }
+                ++$updowncount;
+            }
+
+            // Add settings link.
+            if (!$version) {
+                $settings = '';
+            } else {
+                if ($surl = $plugininfo->get_settings_url()) {
+                    $settings = html_writer::link($surl, $strsettings);
+                } else {
+                    $settings = '';
+                }
+            }
+
+            // Add uninstall info.
+            $uninstall = '';
+            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url($store, 'manage')) {
+                $uninstall = html_writer::link($uninstallurl, $struninstall);
+            }
+
+            // Add a row to the table.
+            $table->data[] = array($icon . $displayname, $version, $hideshow, $updown, $settings, $uninstall);
+
+            $printed[$store] = true;
+        }
+
+        $return .= html_writer::table($table);
+        $return .= get_string('configlogplugins', 'tool_log') . '<br />' . get_string('tablenosave', 'admin');
+        $return .= $OUTPUT->box_end();
+        return highlight($query, $return);
+    }
+}
diff --git a/admin/tool/log/db/events.php b/admin/tool/log/db/events.php
new file mode 100644 (file)
index 0000000..660f2f6
--- /dev/null
@@ -0,0 +1,35 @@
+<?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/>.
+
+/**
+ * Event observer.
+ *
+ * @package   tool_log
+ * @category  event
+ * @copyright 2013 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$observers = array(
+    array(
+        'eventname' => '*',
+        'callback'  => '\tool_log\log\observer::store',
+        'internal'  => false, // This means that we get events only after transaction commit.
+        'priority'  => 1000,
+    ),
+);
diff --git a/admin/tool/log/db/install.php b/admin/tool/log/db/install.php
new file mode 100644 (file)
index 0000000..6fccec4
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * Logging support.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+function xmldb_tool_log_install() {
+    global $CFG;
+
+    $enabled = array();
+
+    // For now enable only the legacy logging, this keeps 100% BC.
+    if (file_exists("$CFG->dirroot/$CFG->admin/tool/log/store/legacy")) {
+        $enabled[] = 'logstore_legacy';
+    }
+
+    set_config('enabled_stores', implode(',', $enabled), 'tool_log');
+}
diff --git a/admin/tool/log/db/subplugins.php b/admin/tool/log/db/subplugins.php
new file mode 100644 (file)
index 0000000..9d1fe61
--- /dev/null
@@ -0,0 +1,25 @@
+<?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/>.
+
+/**
+ * Logging subplugins.
+ *
+ * @package   tool_log
+ * @copyright 2013 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$subplugins = array('logstore' => 'admin/tool/log/store');
diff --git a/admin/tool/log/lang/en/tool_log.php b/admin/tool/log/lang/en/tool_log.php
new file mode 100644 (file)
index 0000000..7301031
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * Store management UI lang strings.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['actlogshdr'] = 'Available log stores';
+$string['configlogplugins'] = 'Please enable all required plugins and arrange then in appropriate order.';
+$string['logging'] = 'Logging';
+$string['managelogging'] = 'Manage log stores';
+$string['pluginname'] = 'Log store manager';
diff --git a/admin/tool/log/lib.php b/admin/tool/log/lib.php
new file mode 100644 (file)
index 0000000..9db2ee3
--- /dev/null
@@ -0,0 +1,37 @@
+<?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/>.
+
+/**
+ * Log tool API.
+ *
+ * @package    tool_log
+ * @copyright  2014 Petr Skoda {@link http://skodak.org/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Execute cron actions.
+ */
+function tool_log_cron() {
+    // Execute cron only if this log manager used.
+    $logmanager = get_log_manager();
+    if (get_class($logmanager) === 'tool_log\log\manager') {
+        /** @var \tool_log\log\manager $logmanager */
+        $logmanager->cron();
+    }
+}
diff --git a/admin/tool/log/settings.php b/admin/tool/log/settings.php
new file mode 100644 (file)
index 0000000..3fee812
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * Logging settings.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+if ($hassiteconfig) {
+    $ADMIN->add('modules', new admin_category('logging', new lang_string('logging', 'tool_log')));
+
+    $temp = new admin_settingpage('managelogging', new lang_string('managelogging', 'tool_log'));
+    $temp->add(new tool_log_setting_managestores());
+    $ADMIN->add('logging', $temp);
+
+    foreach (core_plugin_manager::instance()->get_plugins_of_type('logstore') as $plugin) {
+        /** @var \tool_log\plugininfo\logstore $plugin */
+        $plugin->load_settings($ADMIN, 'logging', $hassiteconfig);
+    }
+}
diff --git a/admin/tool/log/store/database/classes/helper.php b/admin/tool/log/store/database/classes/helper.php
new file mode 100644 (file)
index 0000000..6c6f884
--- /dev/null
@@ -0,0 +1,77 @@
+<?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/>.
+
+/**
+ * Helper class locally used.
+ *
+ * @package    logstore_database
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_database;
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Helper class locally used.
+ *
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class helper {
+    /**
+     * Returns list of fully working database drivers present in system.
+     * @return array
+     */
+    public static function get_drivers() {
+        return array(
+            'native/mysqli'  => \moodle_database::get_driver_instance('mysqli', 'native')->get_name(),
+            'native/mariadb' => \moodle_database::get_driver_instance('mariadb', 'native')->get_name(),
+            'native/pgsql'   => \moodle_database::get_driver_instance('pgsql', 'native')->get_name(),
+            'native/oci'     => \moodle_database::get_driver_instance('oci', 'native')->get_name(),
+            'native/sqlsrv'  => \moodle_database::get_driver_instance('sqlsrv', 'native')->get_name(),
+            'native/mssql'   => \moodle_database::get_driver_instance('mssql', 'native')->get_name()
+        );
+    }
+
+    /**
+     * Get a list of edu levels.
+     *
+     * @return array
+     */
+    public static function get_level_options() {
+        return array(
+            \core\event\base::LEVEL_TEACHING      => get_string('teaching', 'logstore_database'),
+            \core\event\base::LEVEL_PARTICIPATING => get_string('participating', 'logstore_database'),
+            \core\event\base::LEVEL_OTHER         => get_string('other', 'logstore_database'),
+        );
+    }
+
+    /**
+     * Get a list of database actions.
+     *
+     * @return array
+     */
+    public static function get_action_options() {
+        return array(
+            'c' => get_string('create', 'logstore_database'),
+            'r' => get_string('read', 'logstore_database'),
+            'u' => get_string('update', 'logstore_database'),
+            'd' => get_string('delete')
+        );
+    }
+}
diff --git a/admin/tool/log/store/database/classes/log/store.php b/admin/tool/log/store/database/classes/log/store.php
new file mode 100644 (file)
index 0000000..d484c7c
--- /dev/null
@@ -0,0 +1,245 @@
+<?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/>.
+
+/**
+ * External database store.
+ *
+ * @package    logstore_database
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_database\log;
+defined('MOODLE_INTERNAL') || die();
+
+class store implements \tool_log\log\writer, \core\log\sql_select_reader {
+    use \tool_log\helper\store,
+        \tool_log\helper\reader,
+        \tool_log\helper\buffered_writer {
+        dispose as helper_dispose;
+    }
+
+    /** @var \moodle_database $extdb */
+    protected $extdb;
+
+    /** @var bool $logguests true if logging guest access */
+    protected $logguests;
+
+    /** @var array $excludelevels An array of education levels to exclude */
+    protected $excludelevels = array();
+
+    /** @var array $excludeactions An array of actions types to exclude */
+    protected $excludeactions = array();
+
+    /**
+     * Construct
+     *
+     * @param \tool_log\log\manager $manager
+     */
+    public function __construct(\tool_log\log\manager $manager) {
+        $this->helper_setup($manager);
+        $this->buffersize = $this->get_config('buffersize', 50);
+        $this->logguests = $this->get_config('logguests', 1);
+        $actions = $this->get_config('excludeactions', '');
+        $levels = $this->get_config('excludelevels', '');
+        $this->excludeactions = $actions === '' ? array() : explode(',', $actions);
+        $this->excludelevels = $levels === '' ? array() : explode(',', $levels);
+    }
+
+    /**
+     * Setup the Database.
+     *
+     * @return bool
+     */
+    protected function init() {
+        if (isset($this->extdb)) {
+            return !empty($this->extdb);
+        }
+
+        $dbdriver = $this->get_config('dbdriver');
+        if (!$dbdriver) {
+            $this->extdb = false;
+            return false;
+        }
+        list($dblibrary, $dbtype) = explode('/', $dbdriver);
+
+        if (!$db = \moodle_database::get_driver_instance($dbtype, $dblibrary, true)) {
+            debugging("Unknown driver $dblibrary/$dbtype", DEBUG_DEVELOPER);
+            $this->extdb = false;
+            return false;
+        }
+
+        $dboptions = array();
+        $dboptions['dbpersist'] = $this->get_config('dbpersist', '0');
+        $dboptions['dbsocket'] = $this->get_config('dbsocket', '');
+        $dboptions['dbport'] = $this->get_config('dbport', '');
+        $dboptions['dbschema'] = $this->get_config('dbschema', '');
+        $dboptions['dbcollation'] = $this->get_config('dbcollation', '');
+        try {
+            $db->connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'),
+                $this->get_config('dbname'), false, $dboptions);
+            $tables = $db->get_tables();
+            if (!in_array($this->get_config('dbtable'), $tables)) {
+                debugging('Cannot find the specified table', DEBUG_DEVELOPER);
+                $this->extdb = false;
+                return false;
+            }
+        } catch (\moodle_exception $e) {
+            debugging('Cannot connect to external database: ' . $e->getMessage(), DEBUG_DEVELOPER);
+            $this->extdb = false;
+            return false;
+        }
+
+        $this->extdb = $db;
+        return true;
+    }
+
+    /**
+     * Insert events in bulk to the database.
+     *
+     * @param \core\event\base[] $events
+     */
+    protected function insert_events($events) {
+        if (!$this->init()) {
+            return;
+        }
+        if (!$dbtable = $this->get_config('dbtable')) {
+            return;
+        }
+        $dataobj = array();
+
+        // Filter events.
+        foreach ($events as $event) {
+            if (in_array($event->crud, $this->excludeactions) ||
+                in_array($event->edulevel, $this->excludelevels)
+            ) {
+                // Ignore event if the store settings do not want to store it.
+                continue;
+            }
+            if ((!CLI_SCRIPT or PHPUNIT_TEST) and !$this->logguests) {
+                // Always log inside CLI scripts because we do not login there.
+                if (!isloggedin() or isguestuser()) {
+                    continue;
+                }
+            }
+
+            $data = $event->get_data();
+            $data['other'] = serialize($data['other']);
+            if (CLI_SCRIPT) {
+                $data['origin'] = 'cli';
+                $data['ip'] = null;
+            } else {
+                $data['origin'] = 'web';
+                $data['ip'] = getremoteaddr();
+            }
+            $data['realuserid'] = \core\session\manager::is_loggedinas() ? $_SESSION['USER']->realuser : null;
+            $dataobj[] = $data;
+        }
+        try {
+            $this->extdb->insert_records($dbtable, $dataobj);
+        } catch (\moodle_exception $e) {
+            debugging('Cannot write to external database: ' . $e->getMessage(), DEBUG_DEVELOPER);
+        }
+    }
+
+    /**
+     * Get an array of events based on the passed on params.
+     *
+     * @param string $selectwhere select conditions.
+     * @param array $params params.
+     * @param string $sort sortorder.
+     * @param int $limitfrom limit constraints.
+     * @param int $limitnum limit constraints.
+     *
+     * @return array|\core\event\base[] array of events.
+     */
+    public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
+        if (!$this->init()) {
+            return array();
+        }
+
+        if (!$dbtable = $this->get_config('dbtable')) {
+            return array();
+        }
+
+        $events = array();
+        $records = $this->extdb->get_records_select($dbtable, $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
+
+        foreach ($records as $data) {
+            $extra = array('origin' => $data->origin, 'realuserid' => $data->realuserid, 'ip' => $data->ip);
+            $data = (array)$data;
+            $id = $data['id'];
+            $data['other'] = unserialize($data['other']);
+            if ($data['other'] === false) {
+                $data['other'] = array();
+            }
+            unset($data['origin']);
+            unset($data['ip']);
+            unset($data['realuserid']);
+            unset($data['id']);
+
+            $events[$id] = \core\event\base::restore($data, $extra);
+        }
+
+        return $events;
+    }
+
+    /**
+     * Get number of events present for the given select clause.
+     *
+     * @param string $selectwhere select conditions.
+     * @param array $params params.
+     *
+     * @return int Number of events available for the given conditions
+     */
+    public function get_events_select_count($selectwhere, array $params) {
+        if (!$this->init()) {
+            return 0;
+        }
+
+        if (!$dbtable = $this->get_config('dbtable')) {
+            return 0;
+        }
+
+        return $this->extdb->count_records_select($dbtable, $selectwhere, $params);
+    }
+
+    public function cron() {
+    }
+
+    /**
+     * Are the new events appearing in the reader?
+     *
+     * @return bool true means new log events are being added, false means no new data will be added
+     */
+    public function is_logging() {
+        if (!$this->init()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Dispose off database connection after pushing any buffered events to the database.
+     */
+    public function dispose() {
+        $this->helper_dispose();
+        if ($this->extdb) {
+            $this->extdb->dispose();
+        }
+        $this->extdb = null;
+    }
+}
diff --git a/admin/tool/log/store/database/lang/en/logstore_database.php b/admin/tool/log/store/database/lang/en/logstore_database.php
new file mode 100644 (file)
index 0000000..dc775e0
--- /dev/null
@@ -0,0 +1,51 @@
+<?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/>.
+
+/**
+ * Log store lang strings.
+ *
+ * @package    logstore_database
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['buffersize'] = 'Buffer size';
+$string['buffersize_help'] = 'Number of log entries inserted in one batch database operation, which improves performance.';
+$string['conectexception'] = 'Cannot connect to the database.';
+$string['create'] = 'Create';
+$string['databasesettings'] = 'Database settings';
+$string['databasesettings_help'] = 'Connection details for the external log database: {$a}';
+$string['databasepersist'] = 'Persistent database connections';
+$string['databaseschema'] = 'Database schema';
+$string['databasecollation'] = 'Database collation';
+$string['databasetable'] = 'Database table';
+$string['databasetable_help'] = 'Name of the table where logs will be stored. This table should have a structure identical to the one used by logstore_standard (mdl_logstore_standard_log).';
+$string['excludeactions'] = 'Exclude actions of these types';
+$string['excludelevels'] = 'Exclude actions with these educational levels';
+$string['filters'] = 'Filter logs';
+$string['filters_help'] = 'Enable filters that exclude some actions from being logged.';
+$string['logguests'] = 'Log guest actions';
+$string['other'] = 'Other';
+$string['participating'] = 'Participating';
+$string['pluginname'] = 'External database log';
+$string['pluginname_desc'] = 'A log plugin that stores log entries in an external database table.';
+$string['read'] = 'Read';
+$string['tablenotfound'] = 'Specified table was not found';
+$string['teaching'] = 'Teaching';
+$string['testsettings'] = 'Test connection';
+$string['testingsettings'] = 'Testing database settings...';
+$string['update'] = 'Update';
+
diff --git a/admin/tool/log/store/database/settings.php b/admin/tool/log/store/database/settings.php
new file mode 100644 (file)
index 0000000..bb8e2a1
--- /dev/null
@@ -0,0 +1,71 @@
+<?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/>.
+
+/**
+ * External database log store settings.
+ *
+ * @package    logstore_database
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+if ($hassiteconfig) {
+    $testurl = new moodle_url('/admin/tool/log/store/database/test_settings.php', array('sesskey' => sesskey()));
+    $test = new admin_externalpage('logstoredbtestsettings', get_string('testsettings', 'logstore_database'),
+        $testurl, 'moodle/site:config', true);
+    $ADMIN->add('logging', $test);
+
+    $drivers = \logstore_database\helper::get_drivers();
+    // Database settings.
+    $link = html_writer::link($testurl, get_string('testsettings', 'logstore_database'), array('target' => '_blank'));
+    $settings->add(new admin_setting_heading('dbsettings', get_string('databasesettings', 'logstore_database'),
+        get_string('databasesettings_help', 'logstore_database', $link)));
+    $settings->add(new admin_setting_configselect('logstore_database/dbdriver', get_string('databasetypehead', 'install'), '',
+        '', $drivers));
+
+    $settings->add(new admin_setting_configtext('logstore_database/dbhost', get_string('databasehost', 'install'), '', ''));
+    $settings->add(new admin_setting_configtext('logstore_database/dbuser', get_string('databaseuser', 'install'), '', ''));
+    $settings->add(new admin_setting_configpasswordunmask('logstore_database/dbpass', get_string('databasepass', 'install'), '', ''));
+    $settings->add(new admin_setting_configtext('logstore_database/dbname', get_string('databasename', 'install'), '', ''));
+    $settings->add(new admin_setting_configtext('logstore_database/dbtable', get_string('databasetable', 'logstore_database'),
+        get_string('databasetable_help', 'logstore_database'), ''));
+
+    $settings->add(new admin_setting_configcheckbox('logstore_database/dbpersist', get_string('databasepersist',
+        'logstore_database'), '', '0'));
+    $settings->add(new admin_setting_configtext('logstore_database/dbsocket', get_string('databasesocket', 'install'), '',
+        ''));
+    $settings->add(new admin_setting_configtext('logstore_database/dbport', get_string('databaseport', 'install'), '', ''));
+    $settings->add(new admin_setting_configtext('logstore_database/dbschema', get_string('databaseschema',
+        'logstore_database'), '', ''));
+    $settings->add(new admin_setting_configtext('logstore_database/dbcollation', get_string('databasecollation',
+        'logstore_database'), '', ''));
+    $settings->add(new admin_setting_configtext('logstore_database/buffersize', get_string('buffersize',
+        'logstore_database'), get_string('buffersize_help', 'logstore_database'), 50));
+
+    // Filters.
+    $settings->add(new admin_setting_heading('filters', get_string('filters', 'logstore_database'), get_string('filters_help',
+        'logstore_database')));
+    $settings->add(new admin_setting_configcheckbox('logstore_database/logguests', get_string('logguests',
+        'logstore_database'), '', '0'));
+    $levels = \logstore_database\helper::get_level_options();
+    $settings->add(new admin_setting_configmulticheckbox('logstore_database/excludelevels', get_string('excludelevels',
+        'logstore_database'), '', array(), $levels));
+    $actions = \logstore_database\helper::get_action_options();
+    $settings->add(new admin_setting_configmulticheckbox('logstore_database/excludeactions', get_string('excludeactions',
+        'logstore_database'), '', array(), $actions));
+}
diff --git a/admin/tool/log/store/database/test_settings.php b/admin/tool/log/store/database/test_settings.php
new file mode 100644 (file)
index 0000000..ce35bc8
--- /dev/null
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * Filter form.
+ *
+ * @package    logstore_database
+ * @copyright  2014 onwards Ankit Agarwal
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../../../../../config.php');
+require_once($CFG->dirroot . '/lib/adminlib.php');
+
+require_login();
+$context = context_system::instance();
+require_capability('moodle/site:config', $context);
+require_sesskey();
+
+navigation_node::override_active_url(new moodle_url('/admin/settings.php', array('section' => 'logsettingdatabase')));
+admin_externalpage_setup('logstoredbtestsettings');
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('testingsettings', 'logstore_database'));
+
+// NOTE: this is not localised intentionally, admins are supposed to understand English at least a bit...
+
+raise_memory_limit(MEMORY_HUGE);
+$dbtable = get_config('logstore_database', 'dbtable');
+if (empty($dbtable)) {
+    echo $OUTPUT->notification('External table not specified.', 'notifyproblem');
+    die();
+}
+
+$dbdriver = get_config('logstore_database', 'dbdriver');
+list($dblibrary, $dbtype) = explode('/', $dbdriver);
+if (!$db = \moodle_database::get_driver_instance($dbtype, $dblibrary, true)) {
+    echo $OUTPUT->notification("Unknown driver $dblibrary/$dbtype", "notifyproblem");
+    die();
+}
+
+$olddebug = $CFG->debug;
+$olddisplay = ini_get('display_errors');
+ini_set('display_errors', '1');
+$CFG->debug = DEBUG_DEVELOPER;
+error_reporting($CFG->debug);
+
+$dboptions = array();
+$dboptions['dbpersist'] = get_config('logstore_database', 'dbpersist');
+$dboptions['dbsocket'] = get_config('logstore_database', 'dbsocket');
+$dboptions['dbport'] = get_config('logstore_database', 'dbport');
+$dboptions['dbschema'] = get_config('logstore_database', 'dbschema');
+$dboptions['dbcollation'] = get_config('logstore_database', 'dbcollation');
+
+try {
+    $db->connect(get_config('logstore_database', 'dbhost'), get_config('logstore_database', 'dbuser'),
+        get_config('logstore_database', 'dbpass'), get_config('logstore_database', 'dbname'), false, $dboptions);
+} catch (\moodle_exception $e) {
+    echo $OUTPUT->notification('Cannot connect to the database.', 'notifyproblem');
+    $CFG->debug = $olddebug;
+    ini_set('display_errors', $olddisplay);
+    error_reporting($CFG->debug);
+    ob_end_flush();
+    echo $OUTPUT->footer();
+    die();
+}
+echo $OUTPUT->notification('Connection made.', 'notifysuccess');
+$tables = $db->get_tables();
+if (!in_array($dbtable, $tables)) {
+    echo $OUTPUT->notification('Cannot find the specified table ' . $dbtable, 'notifyproblem');
+    $CFG->debug = $olddebug;
+    ini_set('display_errors', $olddisplay);
+    error_reporting($CFG->debug);
+    ob_end_flush();
+    echo $OUTPUT->footer();
+    die();
+}
+echo $OUTPUT->notification('Table ' . $dbtable . ' found.', 'notifysuccess');
+
+$cols = $db->get_columns($dbtable);
+if (empty($cols)) {
+    echo $OUTPUT->notification('Can not read external table.', 'notifyproblem');
+} else {
+    $columns = array_keys((array)$cols);
+    echo $OUTPUT->notification('External table contains following columns:<br />' . implode(', ', $columns), 'notifysuccess');
+}
+
+$db->dispose();
+
+$CFG->debug = $olddebug;
+ini_set('display_errors', $olddisplay);
+error_reporting($CFG->debug);
+ob_end_flush();
+echo $OUTPUT->footer();
diff --git a/admin/tool/log/store/database/tests/fixtures/event.php b/admin/tool/log/store/database/tests/fixtures/event.php
new file mode 100644 (file)
index 0000000..18f9925
--- /dev/null
@@ -0,0 +1,47 @@
+<?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/>.
+
+/**
+ * Fixtures for database log storage testing.
+ *
+ * @package    logstore_database
+ * @copyright  2014 Petr Skoda
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_database\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+
+class unittest_executed extends \core\event\base {
+    public static function get_name() {
+        return 'xxx';
+    }
+
+    public function get_description() {
+        return 'yyy';
+    }
+
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
+    }
+
+    public function get_url() {
+        return new \moodle_url('/somepath/somefile.php', array('id' => $this->data['other']['sample']));
+    }
+}
diff --git a/admin/tool/log/store/database/tests/store_test.php b/admin/tool/log/store/database/tests/store_test.php
new file mode 100644 (file)
index 0000000..e71e58e
--- /dev/null
@@ -0,0 +1,227 @@
+<?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/>.
+
+/**
+ * External database log store tests.
+ *
+ * @package    logstore_database
+ * @copyright  2014 Petr Skoda {@link http://skodak.org/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once(__DIR__ . '/fixtures/event.php');
+
+class logstore_database_store_testcase extends advanced_testcase {
+    public function test_log_writing() {
+        global $DB, $CFG;
+        $this->resetAfterTest();
+        $this->preventResetByRollback(); // Logging waits till the transaction gets committed.
+
+        $dbman = $DB->get_manager();
+        $this->assertTrue($dbman->table_exists('logstore_standard_log'));
+        $DB->delete_records('logstore_standard_log');
+
+        $this->setAdminUser();
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $course1 = $this->getDataGenerator()->create_course();
+        $module1 = $this->getDataGenerator()->create_module('resource', array('course' => $course1));
+        $course2 = $this->getDataGenerator()->create_course();
+        $module2 = $this->getDataGenerator()->create_module('resource', array('course' => $course2));
+
+        // Test all plugins are disabled by this command.
+        set_config('enabled_stores', '', 'tool_log');
+        $manager = get_log_manager(true);
+        $stores = $manager->get_readers();
+        $this->assertCount(0, $stores);
+
+        // Fake the settings, we will abuse the standard plugin table here...
+        $parts = explode('_', get_class($DB));
+        set_config('dbdriver', $parts[1] . '/' . $parts[0], 'logstore_database');
+        set_config('dbhost', $CFG->dbhost, 'logstore_database');
+        set_config('dbuser', $CFG->dbuser, 'logstore_database');
+        set_config('dbpass', $CFG->dbpass, 'logstore_database');
+        set_config('dbname', $CFG->dbname, 'logstore_database');
+        set_config('dbtable', $CFG->prefix . 'logstore_standard_log', 'logstore_database');
+        if (!empty($CFG->dboptions['dbpersist'])) {
+            set_config('dbpersist', 1, 'logstore_database');
+        } else {
+            set_config('dbpersist', 0, 'logstore_database');
+        }
+        if (!empty($CFG->dboptions['dbsocket'])) {
+            set_config('dbsocket', $CFG->dboptions['dbsocket'], 'logstore_database');
+        } else {
+            set_config('dbsocket', '', 'logstore_database');
+        }
+        if (!empty($CFG->dboptions['dbport'])) {
+            set_config('dbport', $CFG->dboptions['dbport'], 'logstore_database');
+        } else {
+            set_config('dbport', '', 'logstore_database');
+        }
+        if (!empty($CFG->dboptions['dbschema'])) {
+            set_config('dbschema', $CFG->dboptions['dbschema'], 'logstore_database');
+        } else {
+            set_config('dbschema', '', 'logstore_database');
+        }
+        if (!empty($CFG->dboptions['dbcollation'])) {
+            set_config('dbcollation', $CFG->dboptions['dbcollation'], 'logstore_database');
+        } else {
+            set_config('dbcollation', '', 'logstore_database');
+        }
+
+        // Enable logging plugin.
+        set_config('enabled_stores', 'logstore_database', 'tool_log');
+        set_config('buffersize', 0, 'logstore_database');
+        set_config('logguests', 1, 'logstore_database');
+        $manager = get_log_manager(true);
+
+        $stores = $manager->get_readers();
+        $this->assertCount(1, $stores);
+        $this->assertEquals(array('logstore_database'), array_keys($stores));
+        $store = $stores['logstore_database'];
+        $this->assertInstanceOf('logstore_database\log\store', $store);
+        $this->assertInstanceOf('tool_log\log\writer', $store);
+        $this->assertTrue($store->is_logging());
+
+        $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
+        $this->assertCount(0, $logs);
+
+        $this->setCurrentTimeStart();
+
+        $this->setUser(0);
+        $event1 = \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)));
+        $event1->trigger();
+
+        $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
+        $this->assertCount(1, $logs);
+
+        $log1 = reset($logs);
+        unset($log1->id);
+        $log1->other = unserialize($log1->other);
+        $log1 = (array)$log1;
+        $data = $event1->get_data();
+        $data['origin'] = 'cli';
+        $data['ip'] = null;
+        $data['realuserid'] = null;
+        $this->assertEquals($data, $log1);
+
+        $this->setAdminUser();
+        \core\session\manager::loginas($user1->id, context_system::instance());
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+
+        $event2 = \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module2->cmid), 'other' => array('sample' => 6, 'xx' => 9)));
+        $event2->trigger();
+
+        $_SESSION['SESSION'] = new \stdClass();
+        $this->setUser(0);
+        $this->assertFalse(\core\session\manager::is_loggedinas());
+
+        $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
+        $this->assertCount(3, $logs);
+        array_shift($logs);
+        $log2 = array_shift($logs);
+        $this->assertSame('\core\event\user_loggedinas', $log2->eventname);
+
+        $log3 = array_shift($logs);
+        unset($log3->id);
+        $log3->other = unserialize($log3->other);
+        $log3 = (array)$log3;
+        $data = $event2->get_data();
+        $data['origin'] = 'cli';
+        $data['ip'] = null;
+        $data['realuserid'] = 2;
+        $this->assertEquals($data, $log3);
+
+        // Test reading.
+        $this->assertSame(3, $store->get_events_select_count('', array()));
+        $events = $store->get_events_select('', array(), 'id', 0, 0);
+        $this->assertCount(3, $events);
+        $resev1 = array_shift($events);
+        array_shift($events);
+        $resev2 = array_shift($events);
+        $this->assertEquals($event1->get_data(), $resev1->get_data());
+        $this->assertEquals($event2->get_data(), $resev2->get_data());
+
+        // Test buffering.
+        set_config('buffersize', 3, 'logstore_database');
+        $manager = get_log_manager(true);
+        $stores = $manager->get_readers();
+        /** @var \logstore_database\log\store $store */
+        $store = $stores['logstore_database'];
+        $DB->delete_records('logstore_standard_log');
+
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+        $store->flush();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(5, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(5, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(5, $DB->count_records('logstore_standard_log'));
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(8, $DB->count_records('logstore_standard_log'));
+
+        // Test guest logging setting.
+        set_config('logguests', 0, 'logstore_database');
+        set_config('buffersize', 0, 'logstore_database');
+        get_log_manager(true);
+        $DB->delete_records('logstore_standard_log');
+        get_log_manager(true);
+
+        $this->setUser(null);
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+
+        $this->setGuestUser();
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+
+        $this->setUser($user1);
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(1, $DB->count_records('logstore_standard_log'));
+
+        $this->setUser($user2);
+        \logstore_database\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+
+        set_config('enabled_stores', '', 'tool_log');
+        get_log_manager(true);
+    }
+}
diff --git a/admin/tool/log/store/database/version.php b/admin/tool/log/store/database/version.php
new file mode 100644 (file)
index 0000000..7656dd4
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * External database log store.
+ *
+ * @package    logstore_standard
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014011900; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2014011000; // Requires this Moodle version.
+$plugin->component = 'logstore_database'; // Full name of the plugin (used for diagnostics).
diff --git a/admin/tool/log/store/legacy/classes/event/legacy_logged.php b/admin/tool/log/store/legacy/classes/event/legacy_logged.php
new file mode 100644 (file)
index 0000000..198e27a
--- /dev/null
@@ -0,0 +1,52 @@
+<?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/>.
+
+namespace logstore_legacy\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Legacy log emulation event class.
+ *
+ * @package    core
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class legacy_logged extends \core\event\base {
+
+    public function init() {
+        throw new \coding_exception('legacy events cannot be triggered');
+    }
+
+    public static function get_name() {
+        return get_string('event_legacy_logged', 'logstore_legacy');
+    }
+
+    public function get_description() {
+        return $this->other['module'] . ' ' . $this->other['action'] . ' ' . $this->other['info'];
+    }
+
+    public function get_url() {
+        global $CFG;
+        require_once("$CFG->dirroot/course/lib.php");
+
+        $url = \make_log_url($this->other['module'], $this->other['url']);
+        if (!$url) {
+            return null;
+        }
+        return new \moodle_url($url);
+    }
+}
diff --git a/admin/tool/log/store/legacy/classes/log/store.php b/admin/tool/log/store/legacy/classes/log/store.php
new file mode 100644 (file)
index 0000000..d69851a
--- /dev/null
@@ -0,0 +1,212 @@
+<?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/>.
+
+/**
+ * Legacy log reader.
+ *
+ * @package    logstore_legacy
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_legacy\log;
+
+defined('MOODLE_INTERNAL') || die();
+
+class store implements \tool_log\log\store, \core\log\sql_select_reader {
+    use \tool_log\helper\store,
+        \tool_log\helper\reader;
+
+    public function __construct(\tool_log\log\manager $manager) {
+        $this->helper_setup($manager);
+    }
+
+    /** @var array list of db fields which needs to be replaced for legacy log query */
+    protected $standardtolegacyfields = array(
+        'timecreated'       => 'time',
+        'courseid'          => 'course',
+        'contextinstanceid' => 'cmid',
+        'origin'            => 'ip'
+    );
+
+    public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
+        global $DB;
+
+        // Replace db field names to make it compatible with legacy log.
+        foreach ($this->standardtolegacyfields as $from => $to) {
+            $selectwhere = str_replace($from, $to, $selectwhere);
+            $sort = str_replace($from, $to, $sort);
+            if (isset($params[$from])) {
+                $params[$to] = $params[$from];
+                unset($params[$from]);
+            }
+        }
+
+        $events = array();
+        $records = array();
+
+        try {
+            $records = $DB->get_records_select('log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
+        } catch (\moodle_exception $ex) {
+            debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
+        }
+
+        foreach ($records as $data) {
+            $events[$data->id] = \logstore_legacy\event\legacy_logged::restore_legacy($data);
+        }
+
+        return $events;
+    }
+
+    public function get_events_select_count($selectwhere, array $params) {
+        global $DB;
+
+        // Replace db field names to make it compatible with legacy log.
+        foreach ($this->standardtolegacyfields as $from => $to) {
+            $selectwhere = str_replace($from, $to, $selectwhere);
+            if (isset($params[$from])) {
+                $params[$to] = $params[$from];
+                unset($params[$from]);
+            }
+        }
+
+        try {
+            return $DB->count_records_select('log', $selectwhere, $params);
+        } catch (\moodle_exception $ex) {
+            debugging("error converting legacy event data " . $ex->getMessage() . $ex->debuginfo, DEBUG_DEVELOPER);
+            return 0;
+        }
+    }
+
+    public function cron() {
+        global $CFG, $DB;
+
+        // Delete old logs to save space (this might need a timer to slow it down...).
+        if (!empty($CFG->loglifetime)) { // Value in days.
+            $loglifetime = time(0) - ($CFG->loglifetime * 3600 * 24);
+            $DB->delete_records_select("log", "time < ?", array($loglifetime));
+            mtrace(" Deleted old log records");
+        }
+    }
+
+    /**
+     * Are the new events appearing in the reader?
+     *
+     * @return bool true means new log events are being added, false means no new data will be added
+     */
+    public function is_logging() {
+        return (bool)$this->get_config('loglegacy', true);
+    }
+
+    public function dispose() {
+    }
+
+    /**
+     * Legacy add_to_log() code.
+     *
+     * @param    int $courseid The course id
+     * @param    string $module The module name  e.g. forum, journal, resource, course, user etc
+     * @param    string $action 'view', 'update', 'add' or 'delete', possibly followed by another word to clarify.
+     * @param    string $url The file and parameters used to see the results of the action
+     * @param    string $info Additional description information
+     * @param    int $cm The course_module->id if there is one
+     * @param    int|\stdClass $user If log regards $user other than $USER
+     */
+    public function legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user) {
+        // Note that this function intentionally does not follow the normal Moodle DB access idioms.
+        // This is for a good reason: it is the most frequently used DB update function,
+        // so it has been optimised for speed.
+        global $DB, $CFG, $USER;
+        if (!$this->is_logging()) {
+            return;
+        }
+
+        if ($cm === '' || is_null($cm)) { // Postgres won't translate empty string to its default.
+            $cm = 0;
+        }
+
+        if ($user) {
+            $userid = $user;
+        } else {
+            if (\core\session\manager::is_loggedinas()) { // Don't log.
+                return;
+            }
+            $userid = empty($USER->id) ? '0' : $USER->id;
+        }
+
+        if (isset($CFG->logguests) and !$CFG->logguests) {
+            if (!$userid or isguestuser($userid)) {
+                return;
+            }
+        }
+
+        $remoteaddr = getremoteaddr();
+
+        $timenow = time();
+        if (!empty($url)) { // Could break doing html_entity_decode on an empty var.
+            $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
+        } else {
+            $url = '';
+        }
+
+        // Restrict length of log lines to the space actually available in the
+        // database so that it doesn't cause a DB error. Log a warning so that
+        // developers can avoid doing things which are likely to cause this on a
+        // routine basis.
+        if (!empty($info) && \core_text::strlen($info) > 255) {
+            $info = \core_text::substr($info, 0, 252) . '...';
+            debugging('Warning: logged very long info', DEBUG_DEVELOPER);
+        }
+
+        // If the 100 field size is changed, also need to alter print_log in course/lib.php.
+        if (!empty($url) && \core_text::strlen($url) > 100) {
+            $url = \core_text::substr($url, 0, 97) . '...';
+            debugging('Warning: logged very long URL', DEBUG_DEVELOPER);
+        }
+
+        if (defined('MDL_PERFDB')) {
+            global $PERF;
+            $PERF->logwrites++;
+        };
+
+        $log = array('time' => $timenow, 'userid' => $userid, 'course' => $courseid, 'ip' => $remoteaddr,
+                     'module' => $module, 'cmid' => $cm, 'action' => $action, 'url' => $url, 'info' => $info);
+
+        try {
+            $DB->insert_record_raw('log', $log, false);
+        } catch (\dml_exception $e) {
+            debugging('Error: Could not insert a new entry to the Moodle log. ' . $e->errorcode, DEBUG_ALL);
+
+            // MDL-11893, alert $CFG->supportemail if insert into log failed.
+            if ($CFG->supportemail and empty($CFG->noemailever)) {
+                // Function email_to_user is not usable because email_to_user tries to write to the logs table,
+                // and this will get caught in an infinite loop, if disk is full.
+                $site = get_site();
+                $subject = 'Insert into log failed at your moodle site ' . $site->fullname;
+                $message = "Insert into log table failed at " . date('l dS \of F Y h:i:s A') .
+                    ".\n It is possible that your disk is full.\n\n";
+                $message .= "The failed query parameters are:\n\n" . var_export($log, true);
+
+                $lasttime = get_config('admin', 'lastloginserterrormail');
+                if (empty($lasttime) || time() - $lasttime > 60 * 60 * 24) { // Limit to 1 email per day.
+                    // Using email directly rather than messaging as they may not be able to log in to access a message.
+                    mail($CFG->supportemail, $subject, $message);
+                    set_config('lastloginserterrormail', time(), 'admin');
+                }
+            }
+        }
+    }
+}
diff --git a/admin/tool/log/store/legacy/db/access.php b/admin/tool/log/store/legacy/db/access.php
new file mode 100644 (file)
index 0000000..1d32982
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * Defines the capabilities used by standard log store.
+ *
+ * @package    logstore_legacy
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = array(
+    'logstore/legacy:read' => array(
+        'riskbitmask'  => RISK_PERSONAL,
+        'captype'      => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes'   => array(
+            'manager'        => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'teacher'        => CAP_ALLOW,
+        ),
+    ),
+);
diff --git a/admin/tool/log/store/legacy/lang/en/logstore_legacy.php b/admin/tool/log/store/legacy/lang/en/logstore_legacy.php
new file mode 100644 (file)
index 0000000..4cf3862
--- /dev/null
@@ -0,0 +1,30 @@
+<?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/>.
+
+/**
+ * Legacy log reader lang strings.
+ *
+ * @package    logstore_legacy
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['event_legacy_logged'] = 'Legacy event logged';
+$string['legacy:read'] = 'Read logs';
+$string['loglegacy'] = 'Log legacy data';
+$string['loglegacy_help'] = 'This plugin records log data to the legacy log table (mdl_log). This functionality has been replaced by newer, richer and more efficient logging plugins, so you should only run this plugin if you have old custom reports that directly query the old log table. Writing to the legacy logs will increase load, so it is recommended that you disable this plugin for performance reasons when it is not needed.';
+$string['pluginname'] = 'Legacy log';
+$string['pluginname_desc'] = 'A log plugin that stores log entries in the legacy log table.';
\ No newline at end of file
diff --git a/admin/tool/log/store/legacy/settings.php b/admin/tool/log/store/legacy/settings.php
new file mode 100644 (file)
index 0000000..d9ed97b
--- /dev/null
@@ -0,0 +1,52 @@
+<?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/>.
+
+/**
+ * Legacy logging settings.
+ *
+ * @package    logstore_legacy
+ * @copyright  2014 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+if ($hassiteconfig) {
+    $settings->add(new admin_setting_configcheckbox('logstore_legacy/loglegacy',
+        new lang_string('loglegacy', 'logstore_legacy'),
+        new lang_string('loglegacy_help', 'logstore_legacy'), 1));
+
+    $settings->add(new admin_setting_configcheckbox('logguests',
+        new lang_string('logguests', 'admin'),
+        new lang_string('logguests_help', 'admin'), 1));
+
+    $options = array(0    => new lang_string('neverdeletelogs'),
+                     1000 => new lang_string('numdays', '', 1000),
+                     365  => new lang_string('numdays', '', 365),
+                     180  => new lang_string('numdays', '', 180),
+                     150  => new lang_string('numdays', '', 150),
+                     120  => new lang_string('numdays', '', 120),
+                     90   => new lang_string('numdays', '', 90),
+                     60   => new lang_string('numdays', '', 60),
+                     35   => new lang_string('numdays', '', 35),
+                     10   => new lang_string('numdays', '', 10),
+                     5    => new lang_string('numdays', '', 5),
+                     2    => new lang_string('numdays', '', 2));
+
+    $settings->add(new admin_setting_configselect('loglifetime',
+        new lang_string('loglifetime', 'admin'),
+        new lang_string('configloglifetime', 'admin'), 0, $options));
+}
diff --git a/admin/tool/log/store/legacy/tests/fixtures/event.php b/admin/tool/log/store/legacy/tests/fixtures/event.php
new file mode 100644 (file)
index 0000000..093afa6
--- /dev/null
@@ -0,0 +1,64 @@
+<?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/>.
+
+/**
+ * Fixtures for legacy logging testing.
+ *
+ * @package    logstore_legacy
+ * @copyright  2014 Petr Skoda
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_legacy\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+
+class unittest_executed extends \core\event\base {
+    public static function get_name() {
+        return 'xxx';
+    }
+
+    public function get_description() {
+        return 'yyy';
+    }
+
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
+    }
+
+    public function get_url() {
+        return new \moodle_url('/somepath/somefile.php', array('id' => $this->data['other']['sample']));
+    }
+
+    public static function get_legacy_eventname() {
+        return 'test_legacy';
+    }
+
+    protected function get_legacy_eventdata() {
+        return array($this->data['courseid'], $this->data['other']['sample']);
+    }
+
+    protected function get_legacy_logdata() {
+        $cmid = 0;
+        if ($this->contextlevel == CONTEXT_MODULE) {
+            $cmid = $this->contextinstanceid;
+        }
+        return array($this->data['courseid'], 'core_unittest', 'view',
+            'unittest.php?id=' . $this->data['other']['sample'], 'bbb', $cmid);
+    }
+}
diff --git a/admin/tool/log/store/legacy/tests/store_test.php b/admin/tool/log/store/legacy/tests/store_test.php
new file mode 100644 (file)
index 0000000..2a1a5fd
--- /dev/null
@@ -0,0 +1,163 @@
+<?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/>.
+
+/**
+ * Legacy log store tests.
+ *
+ * @package    logstore_legacy
+ * @copyright  2014 Petr Skoda {@link http://skodak.org/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once(__DIR__ . '/fixtures/event.php');
+
+class logstore_legacy_store_testcase extends advanced_testcase {
+    public function test_log_writing() {
+        global $DB;
+        $this->resetAfterTest();
+
+        $this->setAdminUser();
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $course1 = $this->getDataGenerator()->create_course();
+        $module1 = $this->getDataGenerator()->create_module('resource', array('course' => $course1));
+        $course2 = $this->getDataGenerator()->create_course();
+        $module2 = $this->getDataGenerator()->create_module('resource', array('course' => $course2));
+
+        // Enable legacy logging plugin.
+        set_config('enabled_stores', 'logstore_legacy', 'tool_log');
+        set_config('loglegacy', 1, 'logstore_legacy');
+        $manager = get_log_manager(true);
+
+        $stores = $manager->get_readers();
+        $this->assertCount(1, $stores);
+        $this->assertEquals(array('logstore_legacy'), array_keys($stores));
+        $store = $stores['logstore_legacy'];
+        $this->assertInstanceOf('logstore_legacy\log\store', $store);
+        $this->assertInstanceOf('core\log\sql_select_reader', $store);
+        $this->assertTrue($store->is_logging());
+
+        $logs = $DB->get_records('log', array(), 'id ASC');
+        $this->assertCount(0, $logs);
+
+        $this->setCurrentTimeStart();
+
+        $this->setUser(0);
+        $event1 = \logstore_legacy\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)));
+        $event1->trigger();
+
+        $this->setUser($user1);
+        $event2 = \logstore_legacy\event\unittest_executed::create(
+            array('context' => context_course::instance($course2->id), 'other' => array('sample' => 6, 'xx' => 11)));
+        $event2->trigger();
+
+        $this->setUser($user2);
+        add_to_log($course1->id, 'xxxx', 'yyyy', '', '7', 0, 0);
+        //$this->assertDebuggingCalled();
+
+        add_to_log($course2->id, 'aaa', 'bbb', 'info.php', '666', $module2->cmid, $user1->id);
+        //$this->assertDebuggingCalled();
+
+        $logs = $DB->get_records('log', array(), 'id ASC');
+        $this->assertCount(4, $logs);
+
+        $log = array_shift($logs);
+        $this->assertNotEmpty($log->id);
+        $this->assertTimeCurrent($log->time);
+        $this->assertEquals(0, $log->userid);
+        $this->assertSame('0.0.0.0', $log->ip);
+        $this->assertEquals($course1->id, $log->course);
+        $this->assertSame('core_unittest', $log->module);
+        $this->assertEquals($module1->cmid, $log->cmid);
+        $this->assertSame('view', $log->action);
+        $this->assertSame('unittest.php?id=5', $log->url);
+        $this->assertSame('bbb', $log->info);
+
+        $oldlogid = $log->id;
+        $log = array_shift($logs);
+        $this->assertGreaterThan($oldlogid, $log->id);
+        $this->assertNotEmpty($log->id);
+        $this->assertTimeCurrent($log->time);
+        $this->assertEquals($user1->id, $log->userid);
+        $this->assertSame('0.0.0.0', $log->ip);
+        $this->assertEquals($course2->id, $log->course);
+        $this->assertSame('core_unittest', $log->module);
+        $this->assertEquals(0, $log->cmid);
+        $this->assertSame('view', $log->action);
+        $this->assertSame('unittest.php?id=6', $log->url);
+        $this->assertSame('bbb', $log->info);
+
+        $oldlogid = $log->id;
+        $log = array_shift($logs);
+        $this->assertGreaterThan($oldlogid, $log->id);
+        $this->assertNotEmpty($log->id);
+        $this->assertTimeCurrent($log->time);
+        $this->assertEquals($user2->id, $log->userid);
+        $this->assertSame('0.0.0.0', $log->ip);
+        $this->assertEquals($course1->id, $log->course);
+        $this->assertSame('xxxx', $log->module);
+        $this->assertEquals(0, $log->cmid);
+        $this->assertSame('yyyy', $log->action);
+        $this->assertSame('', $log->url);
+        $this->assertSame('7', $log->info);
+
+        $oldlogid = $log->id;
+        $log = array_shift($logs);
+        $this->assertGreaterThan($oldlogid, $log->id);
+        $this->assertNotEmpty($log->id);
+        $this->assertTimeCurrent($log->time);
+        $this->assertEquals($user1->id, $log->userid);
+        $this->assertSame('0.0.0.0', $log->ip);
+        $this->assertEquals($course2->id, $log->course);
+        $this->assertSame('aaa', $log->module);
+        $this->assertEquals($module2->cmid, $log->cmid);
+        $this->assertSame('bbb', $log->action);
+        $this->assertSame('info.php', $log->url);
+        $this->assertSame('666', $log->info);
+
+        // Test if disabling works.
+        set_config('enabled_stores', 'logstore_legacy', 'tool_log');
+        set_config('loglegacy', 0, 'logstore_legacy');
+        $manager = get_log_manager(true);
+        $stores = $manager->get_readers();
+        $store = $stores['logstore_legacy'];
+        $this->assertFalse($store->is_logging());
+
+        \logstore_legacy\event\unittest_executed::create(
+            array('context' => \context_system::instance(), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        add_to_log($course1->id, 'xxxx', 'yyyy', '', '7', 0, 0);
+        //$this->assertDebuggingCalled();
+        $this->assertEquals(4, $DB->count_records('log'));
+
+        // Another way to disable legacy completely.
+        set_config('enabled_stores', 'logstore_standard', 'tool_log');
+        set_config('loglegacy', 1, 'logstore_legacy');
+        get_log_manager(true);
+
+        \logstore_legacy\event\unittest_executed::create(
+            array('context' => \context_system::instance(), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        add_to_log($course1->id, 'xxxx', 'yyyy', '', '7', 0, 0);
+        //$this->assertDebuggingCalled();
+        $this->assertEquals(4, $DB->count_records('log'));
+        // Set everything back.
+        set_config('enabled_stores', '', 'tool_log');
+        set_config('loglegacy', 0, 'logstore_legacy');
+        get_log_manager(true);
+    }
+}
diff --git a/admin/tool/log/store/legacy/version.php b/admin/tool/log/store/legacy/version.php
new file mode 100644 (file)
index 0000000..c00663c
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * Legacy log reader.
+ *
+ * @package    logstore_legacy
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014011300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2014011000; // Requires this Moodle version.
+$plugin->component = 'logstore_legacy'; // Full name of the plugin (used for diagnostics).
diff --git a/admin/tool/log/store/standard/classes/log/store.php b/admin/tool/log/store/standard/classes/log/store.php
new file mode 100644 (file)
index 0000000..c2dd866
--- /dev/null
@@ -0,0 +1,135 @@
+<?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/>.
+
+/**
+ * Standard log reader/writer.
+ *
+ * @package    logstore_standard
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_standard\log;
+
+defined('MOODLE_INTERNAL') || die();
+
+class store implements \tool_log\log\writer, \core\log\sql_internal_reader {
+    use \tool_log\helper\store,
+        \tool_log\helper\buffered_writer,
+        \tool_log\helper\reader;
+
+    /** @var string $logguests true if logging guest access */
+    protected $logguests;
+
+    public function __construct(\tool_log\log\manager $manager) {
+        $this->helper_setup($manager);
+        // Log everything before setting is saved for the first time.
+        $this->logguests = $this->get_config('logguests', 1);
+    }
+
+    /**
+     * Finally store the events into the database.
+     *
+     * @param \core\event\base[] $events
+     */
+    protected function insert_events($events) {
+        global $DB;
+
+        $dataobj = array();
+
+        // Filter events.
+        foreach ($events as $event) {
+            if ((!CLI_SCRIPT or PHPUNIT_TEST) and !$this->logguests) {
+                // Always log inside CLI scripts because we do not login there.
+                if (!isloggedin() or isguestuser()) {
+                    continue;
+                }
+            }
+
+            $data = $event->get_data();
+            $data['other'] = serialize($data['other']);
+            if (CLI_SCRIPT) {
+                $data['origin'] = 'cli';
+                $data['ip'] = null;
+            } else {
+                $data['origin'] = 'web';
+                $data['ip'] = getremoteaddr();
+            }
+            $data['realuserid'] = \core\session\manager::is_loggedinas() ? $_SESSION['USER']->realuser : null;
+            $dataobj[] = $data;
+        }
+
+        $DB->insert_records('logstore_standard_log', $dataobj);
+    }
+
+    public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
+        global $DB;
+
+        $events = array();
+        $records = $DB->get_records_select('logstore_standard_log', $selectwhere, $params, $sort, '*', $limitfrom, $limitnum);
+
+        foreach ($records as $data) {
+            $extra = array('origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid);
+            $data = (array)$data;
+            $id = $data['id'];
+            $data['other'] = unserialize($data['other']);
+            if ($data['other'] === false) {
+                $data['other'] = array();
+            }
+            unset($data['origin']);
+            unset($data['ip']);
+            unset($data['realuserid']);
+            unset($data['id']);
+
+            $events[$id] = \core\event\base::restore($data, $extra);
+        }
+
+        return $events;
+    }
+
+    public function get_events_select_count($selectwhere, array $params) {
+        global $DB;
+        return $DB->count_records_select('logstore_standard_log', $selectwhere, $params);
+    }
+
+    public function get_internal_log_table_name() {
+        return 'logstore_standard_log';
+    }
+
+    public function cron() {
+        global $DB;
+        $loglifetime = $this->get_config('loglifetime', 0);
+
+        // NOTE: we should do this only once a day, new cron will deal with this.
+
+        if ($loglifetime > 0) {
+            $loglifetime = time() - ($loglifetime * 3600 * 24); // Value in days.
+            $DB->delete_records_select("logstore_standard_log", "timecreated < ?", array($loglifetime));
+            mtrace(" Deleted old log records from standard store.");
+        }
+    }
+
+    /**
+     * Are the new events appearing in the reader?
+     *
+     * @return bool true means new log events are being added, false means no new data will be added
+     */
+    public function is_logging() {
+        // Only enabled stpres are queried,
+        // this means we can return true here unless store has some extra switch.
+        return true;
+    }
+}
diff --git a/admin/tool/log/store/standard/db/access.php b/admin/tool/log/store/standard/db/access.php
new file mode 100644 (file)
index 0000000..774489b
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * Defines the capabilities used by standard log store.
+ *
+ * @package    logstore_standard
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = array(
+    'logstore/standard:read' => array(
+        'riskbitmask'  => RISK_PERSONAL,
+        'captype'      => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes'   => array(
+            'manager'        => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'teacher'        => CAP_ALLOW,
+        ),
+    ),
+);
diff --git a/admin/tool/log/store/standard/db/install.xml b/admin/tool/log/store/standard/db/install.xml
new file mode 100644 (file)
index 0000000..6232e6e
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="admin/tool/log/store/standard/db" VERSION="20140211" COMMENT="XMLDB file for Moodle admin/tool/log/store/standard"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="../../../../../../lib/xmldb/xmldb.xsd"
+>
+  <TABLES>
+    <TABLE NAME="logstore_standard_log" COMMENT="Standard log table">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="eventname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="action" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="target" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="objecttable" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false"/>
+        <FIELD NAME="objectid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
+        <FIELD NAME="crud" TYPE="char" LENGTH="1" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="edulevel" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="contextlevel" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="contextinstanceid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
+        <FIELD NAME="relateduserid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
+        <FIELD NAME="other" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="origin" TYPE="char" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="cli, cron, ws, etc."/>
+        <FIELD NAME="ip" TYPE="char" LENGTH="45" NOTNULL="false" SEQUENCE="false" COMMENT="IP address"/>
+        <FIELD NAME="realuserid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="real user id when logged-in-as"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="timecreated" UNIQUE="false" FIELDS="timecreated"/>
+        <INDEX NAME="contextid-component" UNIQUE="false" FIELDS="contextid, component"/>
+        <INDEX NAME="courseid" UNIQUE="false" FIELDS="courseid"/>
+        <INDEX NAME="eventname" UNIQUE="false" FIELDS="eventname"/>
+        <INDEX NAME="crud" UNIQUE="false" FIELDS="crud"/>
+        <INDEX NAME="edulevel" UNIQUE="false" FIELDS="edulevel"/>
+      </INDEXES>
+    </TABLE>
+  </TABLES>
+</XMLDB>
\ No newline at end of file
diff --git a/admin/tool/log/store/standard/lang/en/logstore_standard.php b/admin/tool/log/store/standard/lang/en/logstore_standard.php
new file mode 100644 (file)
index 0000000..4276fa7
--- /dev/null
@@ -0,0 +1,28 @@
+<?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/>.
+
+/**
+ * Log store lang strings.
+ *
+ * @package    logstore_standard
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['buffersize'] = 'Write buffer size';
+$string['pluginname'] = 'Standard log';
+$string['pluginname_desc'] = 'A log plugin stores log entries in a Moodle database table.';
+$string['standard:read'] = 'Read logs';
diff --git a/admin/tool/log/store/standard/settings.php b/admin/tool/log/store/standard/settings.php
new file mode 100644 (file)
index 0000000..daa8131
--- /dev/null
@@ -0,0 +1,53 @@
+<?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/>.
+
+/**
+ * Standard log store settings.
+ *
+ * @package    logstore_standard
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+if ($hassiteconfig) {
+
+    $settings->add(new admin_setting_configcheckbox('logstore_standard/logguests',
+        new lang_string('logguests', 'core_admin'),
+        new lang_string('logguests_help', 'core_admin'), 1));
+
+    $options = array(
+        0    => new lang_string('neverdeletelogs'),
+        1000 => new lang_string('numdays', '', 1000),
+        365  => new lang_string('numdays', '', 365),
+        180  => new lang_string('numdays', '', 180),
+        150  => new lang_string('numdays', '', 150),
+        120  => new lang_string('numdays', '', 120),
+        90   => new lang_string('numdays', '', 90),
+        60   => new lang_string('numdays', '', 60),
+        35   => new lang_string('numdays', '', 35),
+        10   => new lang_string('numdays', '', 10),
+        5    => new lang_string('numdays', '', 5),
+        2    => new lang_string('numdays', '', 2));
+    $settings->add(new admin_setting_configselect('logstore_standard/loglifetime',
+        new lang_string('loglifetime', 'core_admin'),
+        new lang_string('configloglifetime', 'core_admin'), 0, $options));
+
+    $settings->add(new admin_setting_configtext('logstore_standard/buffersize',
+        get_string('buffersize', 'logstore_standard'),
+        '', '50', PARAM_INT));
+}
diff --git a/admin/tool/log/store/standard/tests/fixtures/event.php b/admin/tool/log/store/standard/tests/fixtures/event.php
new file mode 100644 (file)
index 0000000..efeac2e
--- /dev/null
@@ -0,0 +1,47 @@
+<?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/>.
+
+/**
+ * Fixtures for standard log storage testing.
+ *
+ * @package    logstore_standard
+ * @copyright  2014 Petr Skoda
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace logstore_standard\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+
+class unittest_executed extends \core\event\base {
+    public static function get_name() {
+        return 'xxx';
+    }
+
+    public function get_description() {
+        return 'yyy';
+    }
+
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
+    }
+
+    public function get_url() {
+        return new \moodle_url('/somepath/somefile.php', array('id' => $this->data['other']['sample']));
+    }
+}
diff --git a/admin/tool/log/store/standard/tests/store_test.php b/admin/tool/log/store/standard/tests/store_test.php
new file mode 100644 (file)
index 0000000..2c2ea34
--- /dev/null
@@ -0,0 +1,193 @@
+<?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/>.
+
+/**
+ * Standard log store tests.
+ *
+ * @package    logstore_standard
+ * @copyright  2014 Petr Skoda {@link http://skodak.org/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once(__DIR__ . '/fixtures/event.php');
+
+class logstore_standard_store_testcase extends advanced_testcase {
+    public function test_log_writing() {
+        global $DB;
+        $this->resetAfterTest();
+        $this->preventResetByRollback(); // Logging waits till the transaction gets committed.
+
+        $this->setAdminUser();
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $course1 = $this->getDataGenerator()->create_course();
+        $module1 = $this->getDataGenerator()->create_module('resource', array('course' => $course1));
+        $course2 = $this->getDataGenerator()->create_course();
+        $module2 = $this->getDataGenerator()->create_module('resource', array('course' => $course2));
+
+        // Test all plugins are disabled by this command.
+        set_config('enabled_stores', '', 'tool_log');
+        $manager = get_log_manager(true);
+        $stores = $manager->get_readers();
+        $this->assertCount(0, $stores);
+
+        // Enable logging plugin.
+        set_config('enabled_stores', 'logstore_standard', 'tool_log');
+        set_config('buffersize', 0, 'logstore_standard');
+        set_config('logguests', 1, 'logstore_standard');
+        $manager = get_log_manager(true);
+
+        $stores = $manager->get_readers();
+        $this->assertCount(1, $stores);
+        $this->assertEquals(array('logstore_standard'), array_keys($stores));
+        $store = $stores['logstore_standard'];
+        $this->assertInstanceOf('logstore_standard\log\store', $store);
+        $this->assertInstanceOf('tool_log\log\writer', $store);
+        $this->assertTrue($store->is_logging());
+
+        $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
+        $this->assertCount(0, $logs);
+
+        $this->setCurrentTimeStart();
+
+        $this->setUser(0);
+        $event1 = \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)));
+        $event1->trigger();
+
+        $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
+        $this->assertCount(1, $logs);
+
+        $log1 = reset($logs);
+        unset($log1->id);
+        $log1->other = unserialize($log1->other);
+        $log1 = (array)$log1;
+        $data = $event1->get_data();
+        $data['origin'] = 'cli';
+        $data['ip'] = null;
+        $data['realuserid'] = null;
+        $this->assertEquals($data, $log1);
+
+        $this->setAdminUser();
+        \core\session\manager::loginas($user1->id, context_system::instance());
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+
+        $event2 = \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module2->cmid), 'other' => array('sample' => 6, 'xx' => 9)));
+        $event2->trigger();
+
+        $_SESSION['SESSION'] = new \stdClass();
+        $this->setUser(0);
+        $this->assertFalse(\core\session\manager::is_loggedinas());
+
+        $logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
+        $this->assertCount(3, $logs);
+        array_shift($logs);
+        $log2 = array_shift($logs);
+        $this->assertSame('\core\event\user_loggedinas', $log2->eventname);
+
+        $log3 = array_shift($logs);
+        unset($log3->id);
+        $log3->other = unserialize($log3->other);
+        $log3 = (array)$log3;
+        $data = $event2->get_data();
+        $data['origin'] = 'cli';
+        $data['ip'] = null;
+        $data['realuserid'] = 2;
+        $this->assertEquals($data, $log3);
+
+        // Test table exists.
+        $tablename = $store->get_internal_log_table_name();
+        $this->assertTrue($DB->get_manager()->table_exists($tablename));
+
+        // Test reading.
+        $this->assertSame(3, $store->get_events_select_count('', array()));
+        $events = $store->get_events_select('', array(), 'id', 0, 0);
+        $this->assertCount(3, $events);
+        $resev1 = array_shift($events);
+        array_shift($events);
+        $resev2 = array_shift($events);
+        $this->assertEquals($event1->get_data(), $resev1->get_data());
+        $this->assertEquals($event2->get_data(), $resev2->get_data());
+
+        // Test buffering.
+        set_config('buffersize', 3, 'logstore_standard');
+        $manager = get_log_manager(true);
+        $stores = $manager->get_readers();
+        /** @var \logstore_standard\log\store $store */
+        $store = $stores['logstore_standard'];
+        $DB->delete_records('logstore_standard_log');
+
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+        $store->flush();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(5, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(5, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(5, $DB->count_records('logstore_standard_log'));
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(8, $DB->count_records('logstore_standard_log'));
+
+        // Test guest logging setting.
+        set_config('logguests', 0, 'logstore_standard');
+        set_config('buffersize', 0, 'logstore_standard');
+        get_log_manager(true);
+        $DB->delete_records('logstore_standard_log');
+        get_log_manager(true);
+
+        $this->setUser(null);
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+
+        $this->setGuestUser();
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(0, $DB->count_records('logstore_standard_log'));
+
+        $this->setUser($user1);
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(1, $DB->count_records('logstore_standard_log'));
+
+        $this->setUser($user2);
+        \logstore_standard\event\unittest_executed::create(
+            array('context' => context_module::instance($module1->cmid), 'other' => array('sample' => 5, 'xx' => 10)))->trigger();
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+
+        set_config('enabled_stores', '', 'tool_log');
+        get_log_manager(true);
+    }
+}
diff --git a/admin/tool/log/store/standard/version.php b/admin/tool/log/store/standard/version.php
new file mode 100644 (file)
index 0000000..ae201da
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * Standard log store.
+ *
+ * @package    logstore_standard
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014021100; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2014012400; // Requires this Moodle version.
+$plugin->component = 'logstore_standard'; // Full name of the plugin (used for diagnostics).
diff --git a/admin/tool/log/stores.php b/admin/tool/log/stores.php
new file mode 100644 (file)
index 0000000..5b8e712
--- /dev/null
@@ -0,0 +1,98 @@
+<?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/>.
+
+/**
+ * Logging store management.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+$action = required_param('action', PARAM_ALPHANUMEXT);
+$enrol = required_param('store', PARAM_PLUGIN);
+
+$PAGE->set_url('/admin/tool/log/stores.php');
+$PAGE->set_context(context_system::instance());
+
+require_login();
+require_capability('moodle/site:config', context_system::instance());
+require_sesskey();
+
+$all = \tool_log\log\manager::get_store_plugins();
+$enabled = get_config('tool_log', 'enabled_stores');
+if (!$enabled) {
+    $enabled = array();
+} else {
+    $enabled = array_flip(explode(',', $enabled));
+}
+
+$return = new moodle_url('/admin/settings.php', array('section' => 'managelogging'));
+
+$syscontext = context_system::instance();
+
+switch ($action) {
+    case 'disable':
+        unset($enabled[$enrol]);
+        set_config('enabled_stores', implode(',', array_keys($enabled)), 'tool_log');
+        break;
+
+    case 'enable':
+        if (!isset($all[$enrol])) {
+            break;
+        }
+        $enabled = array_keys($enabled);
+        $enabled[] = $enrol;
+        set_config('enabled_stores', implode(',', $enabled), 'tool_log');
+        break;
+
+    case 'up':
+        if (!isset($enabled[$enrol])) {
+            break;
+        }
+        $enabled = array_keys($enabled);
+        $enabled = array_flip($enabled);
+        $current = $enabled[$enrol];
+        if ($current == 0) {
+            break; // Already at the top.
+        }
+        $enabled = array_flip($enabled);
+        $enabled[$current] = $enabled[$current - 1];
+        $enabled[$current - 1] = $enrol;
+        set_config('enabled_stores', implode(',', $enabled), 'tool_log');
+        break;
+
+    case 'down':
+        if (!isset($enabled[$enrol])) {
+            break;
+        }
+        $enabled = array_keys($enabled);
+        $enabled = array_flip($enabled);
+        $current = $enabled[$enrol];
+        if ($current == count($enabled) - 1) {
+            break; // Already at the end.
+        }
+        $enabled = array_flip($enabled);
+        $enabled[$current] = $enabled[$current + 1];
+        $enabled[$current + 1] = $enrol;
+        set_config('enabled_stores', implode(',', $enabled), 'tool_log');
+        break;
+}
+
+redirect($return);
diff --git a/admin/tool/log/tests/manager_test.php b/admin/tool/log/tests/manager_test.php
new file mode 100644 (file)
index 0000000..1e2e726
--- /dev/null
@@ -0,0 +1,71 @@
+<?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/>.
+
+/**
+ * Log manager and log API tests.
+ *
+ * @package    tool_log
+ * @copyright  2014 Petr Skoda {@link http://skodak.org/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+class tool_log_manager_testcase extends advanced_testcase {
+    public function test_get_log_manager() {
+        global $CFG;
+        $this->resetAfterTest();
+
+        $manager = get_log_manager();
+        $this->assertInstanceOf('core\log\manager', $manager);
+
+        $stores = $manager->get_readers();
+        $this->assertInternalType('array', $stores);
+        $this->assertCount(0, $stores);
+
+        $this->assertFileExists("$CFG->dirroot/$CFG->admin/tool/log/store/standard/version.php");
+        $this->assertFileExists("$CFG->dirroot/$CFG->admin/tool/log/store/legacy/version.php");
+
+        set_config('enabled_stores', 'logstore_standard,logstore_legacy', 'tool_log');
+        $manager = get_log_manager(true);
+        $this->assertInstanceOf('core\log\manager', $manager);
+
+        $stores = $manager->get_readers();
+        $this->assertInternalType('array', $stores);
+        $this->assertCount(2, $stores);
+        foreach ($stores as $key => $store) {
+            $this->assertInternalType('string', $key);
+            $this->assertInstanceOf('core\log\sql_select_reader', $store);
+        }
+
+        $stores = $manager->get_readers('core\log\sql_internal_reader');
+        $this->assertInternalType('array', $stores);
+        $this->assertCount(1, $stores);
+        foreach ($stores as $key => $store) {
+            $this->assertInternalType('string', $key);
+            $this->assertSame('logstore_standard', $key);
+            $this->assertInstanceOf('core\log\sql_internal_reader', $store);
+        }
+
+        $stores = $manager->get_readers('core\log\sql_select_reader');
+        $this->assertInternalType('array', $stores);
+        $this->assertCount(2, $stores);
+        foreach ($stores as $key => $store) {
+            $this->assertInternalType('string', $key);
+            $this->assertInstanceOf('core\log\sql_select_reader', $store);
+        }
+    }
+}
diff --git a/admin/tool/log/version.php b/admin/tool/log/version.php
new file mode 100644 (file)
index 0000000..0838cd2
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * Default log manager.
+ *
+ * @package    tool_log
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2014011300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2014011000; // Requires this Moodle version.
+$plugin->component = 'tool_log'; // Full name of the plugin (used for diagnostics).
index c1bf56f..6068809 100644 (file)
@@ -30,7 +30,7 @@ Feature: An admin can create courses using a CSV file
   @javascript
   Scenario: Creation of existing courses
     Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
-    And I select "Create all, increment shortname if needed" from "Upload mode"
+    And I set the field "Upload mode" to "Create all, increment shortname if needed"
     And I click on "Preview" "button"
     When I click on "Upload courses" "button"
     Then I should see "Course created"
index 4ac4f4e..4f11589 100644 (file)
@@ -16,8 +16,8 @@ Feature: An admin can update courses using a CSV file
   @javascript
   Scenario: Updating a course fullname
     Given I upload "admin/tool/uploadcourse/tests/fixtures/courses.csv" file to "File" filemanager
-    And I select "Only update existing courses" from "Upload mode"
-    And I select "Update with CSV data only" from "Update mode"
+    And I set the field "Upload mode" to "Only update existing courses"
+    And I set the field "Update mode" to "Update with CSV data only"
     And I click on "Preview" "button"
     When I click on "Upload courses" "button"
     Then I should see "Course updated"
index 2ac377c..e0d6238 100644 (file)
@@ -56,8 +56,8 @@ class behat_auth extends behat_base {
         // Generic steps (we will prefix them later expanding the navigation dropdown if necessary).
         $steps = array(
             new Given('I follow "' . get_string('login') . '"'),
-            new Given('I fill in "' . get_string('username') . '" with "' . $this->escape($username) . '"'),
-            new Given('I fill in "' . get_string('password') . '" with "'. $this->escape($username) . '"'),
+            new Given('I set the field "' . get_string('username') . '" to "' . $this->escape($username) . '"'),
+            new Given('I set the field "' . get_string('password') . '" to "'. $this->escape($username) . '"'),
             new Given('I press "' . get_string('login') . '"')
         );
 
index ed50101..eff7f6f 100644 (file)
@@ -19,8 +19,8 @@ Feature: Authentication
       | testuser | testuser | Test | User | moodle@moodlemoodle.com |
     And I am on homepage
     When I follow "Log in"
-    And I fill in "Username" with "testuser"
-    And I fill in "Password" with "testuser"
+    And I set the field "Username" to "testuser"
+    And I set the field "Password" to "testuser"
     And I press "Log in"
     Then I should see "You are logged in as"
 
@@ -30,8 +30,8 @@ Feature: Authentication
       | testuser | testuser | Test | User | moodle@moodlemoodle.com |
     And I am on homepage
     When I follow "Log in"
-    And I fill in "Username" with "testuser"
-    And I fill in "Password" with "unexisting"
+    And I set the field "Username" to "testuser"
+    And I set the field "Password" to "unexisting"
     And I press "Log in"
     Then I should see "Invalid login, please try again"
 
index 68c24d6..c2628ab 100644 (file)
@@ -4236,7 +4236,7 @@ abstract class restore_questions_activity_structure_step extends restore_activit
 
     /**
      * Process the attempt data defined by {@link add_legacy_question_attempt_data()}.
-     * @param object $data contains all the grouped attempt data ot process.
+     * @param object $data contains all the grouped attempt data to process.
      * @param pbject $quiz data about the activity the attempts belong to. Required
      * fields are (basically this only works for the quiz module):
      *      oldquestions => list of question ids in this activity - using old ids.
@@ -4298,7 +4298,8 @@ abstract class restore_questions_activity_structure_step extends restore_activit
         $this->inform_new_usage_id($usage->id);
 
         $data->uniqueid = $usage->id;
-        $upgrader->save_usage($quiz->preferredbehaviour, $data, $qas, $quiz->questions);
+        $upgrader->save_usage($quiz->preferredbehaviour, $data, $qas,
+                 $this->questions_recode_layout($quiz->oldquestions));
     }
 
     protected function find_question_session_and_states($data, $questionid) {
index 3314372..8bff1de 100644 (file)
@@ -25,12 +25,12 @@ Feature: Duplicate activities
     And I wait until section "1" is available
     And I open "Test database name" actions menu
     And I click on "Edit settings" "link" in the "Test database name" activity
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Original database name |
     And I press "Save and return to course"
     And I open "Test database name" actions menu
     And I click on "Edit settings" "link" in the "Test database name" activity
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Duplicated database name |
       | Description | Duplicated database description |
     And I press "Save and return to course"
index 8d68ffd..eb190e4 100644 (file)
@@ -42,9 +42,9 @@ Feature: Restore Moodle 2 course backups
     And I should see "Test forum name"
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
-    And the "id_format" field should match "Topics format" value
-    And the "Number of sections" field should match "15" value
-    And the "Course layout" field should match "Show one section per page" value
+    And the field "id_format" matches value "Topics format"
+    And the field "Number of sections" matches value "15"
+    And the field "Course layout" matches value "Show one section per page"
     And I press "Cancel"
 
   @javascript
@@ -87,8 +87,8 @@ Feature: Restore Moodle 2 course backups
     And I should see "Test forum name"
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
-    And the "id_format" field should match "Topics format" value
-    And I fill the moodle form with:
+    And the field "id_format" matches value "Topics format"
+    And I set the following fields to these values:
       | id_startdate_day | 1 |
       | id_startdate_month | January |
       | id_startdate_year | 2020 |
@@ -98,14 +98,14 @@ Feature: Restore Moodle 2 course backups
     And I should see "Test forum name"
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
-    And the "id_format" field should match "Weekly format" value
-    And I fill the moodle form with:
+    And the field "id_format" matches value "Weekly format"
+    And I set the following fields to these values:
       | id_format | Social format |
     And I press "Save changes"
     And I should see "An open forum for chatting about anything you want to"
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
-    And the "id_format" field should match "Social format" value
+    And the field "id_format" matches value "Social format"
     And I press "Cancel"
 
   @javascript
@@ -122,9 +122,9 @@ Feature: Restore Moodle 2 course backups
       | Overwrite course configuration | Yes |
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
-    Then the "id_format" field should match "Topics format" value
-    And the "Number of sections" field should match "15" value
-    And the "Course layout" field should match "Show one section per page" value
+    Then the field "id_format" matches value "Topics format"
+    And the field "Number of sections" matches value "15"
+    And the field "Course layout" matches value "Show one section per page"
     And I press "Cancel"
     And section "3" should be hidden
     And section "7" should be hidden
index 39c2c4c..a2859e4 100644 (file)
@@ -40,8 +40,8 @@ class core_badges_observer {
             require_once($CFG->dirroot.'/lib/badgeslib.php');
 
             $eventdata = $event->get_record_snapshot('course_modules_completion', $event->objectid);
-            $userid = $event->other['relateduserid'];
-            $mod = $eventdata->coursemoduleid;
+            $userid = $event->relateduserid;
+            $mod = $event->contextinstanceid;
 
             if ($eventdata->completionstate == COMPLETION_COMPLETE
                 || $eventdata->completionstate == COMPLETION_COMPLETE_PASS
index 7371491..37b534c 100644 (file)
@@ -13,12 +13,12 @@ Feature: Add badges to the system
     Given I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Badges settings"
-    And I fill in "Default badge issuer name" with "Test Badge Site"
-    And I fill in "Default badge issuer contact details" with "testuser@test-badge-site.com"
+    And I set the field "Default badge issuer name" to "Test Badge Site"
+    And I set the field "Default badge issuer contact details" to "testuser@test-badge-site.com"
     And I press "Save changes"
     When I follow "Add a new badge"
-    Then the "issuercontact" field should match "testuser@test-badge-site.com" value
-    And the "issuername" field should match "Test Badge Site" value
+    Then the field "issuercontact" matches value "testuser@test-badge-site.com"
+    And the field "issuername" matches value "Test Badge Site"
 
   @javascript
   Scenario: Accessing the badges
@@ -31,7 +31,7 @@ Feature: Add badges to the system
     Given I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Test Badge |
       | Description | Test badge description |
       | issuername | Test Badge Site |
index 582b9fb..b6ac009 100644 (file)
@@ -10,17 +10,17 @@ Feature: Award badges
     And I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Profile Badge |
       | Description | Test badge description |
       | issuername | Test Badge Site |
       | issuercontact | testuser@test-badge-site.com |
     And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
-    And I select "Profile completion" from "type"
-    And I check "First name"
-    And I check "Email address"
-    And I check "Phone"
+    And I set the field "type" to "Profile completion"
+    And I set the field "First name" to "1"
+    And I set the field "Email address" to "1"
+    And I set the field "Phone" to "1"
     When I press "Save"
     Then I should see "Profile completion"
     And I should see "First name"
@@ -31,7 +31,7 @@ Feature: Award badges
     And I expand "My profile settings" node
     And I follow "Edit profile"
     And I expand all fieldsets
-    And I fill in "Phone" with "123456789"
+    And I set the field "Phone" to "123456789"
     And I press "Update profile"
     And I follow "My badges"
     Then I should see "Profile Badge"
@@ -47,22 +47,22 @@ Feature: Award badges
     And I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Site Badge |
       | Description | Site badge description |
       | issuername | Tester of site badge |
     And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
-    And I select "Manual issue by role" from "type"
-    And I check "Teacher"
+    And I set the field "type" to "Manual issue by role"
+    And I set the field "Teacher" to "1"
     And I press "Save"
     And I press "Enable access"
     And I press "Continue"
     And I follow "Recipients (0)"
     And I press "Award badge"
-    And I select "teacher 1 (teacher1@asd.com)" from "potentialrecipients[]"
+    And I set the field "potentialrecipients[]" to "teacher 1 (teacher1@asd.com)"
     And I press "Award badge"
-    And I select "student 1 (student1@asd.com)" from "potentialrecipients[]"
+    And I set the field "potentialrecipients[]" to "student 1 (student1@asd.com)"
     And I press "Award badge"
     When I follow "Site Badge"
     Then I should see "Recipients (2)"
@@ -91,22 +91,22 @@ Feature: Award badges
     And I follow "Course 1"
     And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
     And I follow "Add a new badge"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Course Badge |
       | Description | Course badge description |
       | issuername | Tester of course badge |
     And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
-    And I select "Manual issue by role" from "type"
-    And I check "Teacher"
+    And I set the field "type" to "Manual issue by role"
+    And I set the field "Teacher" to "1"
     And I press "Save"
     And I press "Enable access"
     And I press "Continue"
     And I follow "Recipients (0)"
     And I press "Award badge"
-    And I select "Student 2 (student2@asd.com)" from "potentialrecipients[]"
+    And I set the field "potentialrecipients[]" to "Student 2 (student2@asd.com)"
     And I press "Award badge"
-    And I select "Student 1 (student1@asd.com)" from "potentialrecipients[]"
+    And I set the field "potentialrecipients[]" to "Student 1 (student1@asd.com)"
     When I press "Award badge"
     And I follow "Course Badge"
     Then I should see "Recipients (2)"
@@ -136,7 +136,7 @@ Feature: Award badges
     And I follow "Home"
     And I follow "Course 1"
     And I follow "Edit settings"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Enable completion tracking | Yes |
     And I press "Save changes"
     And I turn editing mode on
@@ -148,14 +148,14 @@ Feature: Award badges
     And I follow "Course 1"
     And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
     And I follow "Add a new badge"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Course Badge |
       | Description | Course badge description |
       | issuername | Tester of course badge |
     And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
-    And I select "Activity completion" from "type"
-    And I check "Test assignment name"
+    And I set the field "type" to "Activity completion"
+    And I set the field "Test assignment name" to "1"
     And I press "Save"
     And I press "Enable access"
     When I press "Continue"
@@ -191,7 +191,7 @@ Feature: Award badges
     And I follow "Home"
     And I follow "Course 1"
     And I follow "Edit settings"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Enable completion tracking | Yes |
     And I press "Save changes"
     And I turn editing mode on
@@ -200,23 +200,23 @@ Feature: Award badges
       | Description | Submit your online text |
       | assignsubmission_onlinetext_enabled | 1 |
     And I follow "Course completion"
-    And I select "2" from "id_overall_aggregation"
+    And I set the field "id_overall_aggregation" to "2"
     And I click on "Condition: Activity completion" "link"
-    And I check "Assign - Test assignment name"
+    And I set the field "Assign - Test assignment name" to "1"
     And I press "Save changes"
     And I log out
     And I log in as "teacher1"
     And I follow "Course 1"
     And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
     And I follow "Add a new badge"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Course Badge |
       | Description | Course badge description |
       | issuername | Tester of course badge |
     And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
     And I press "Create badge"
-    And I select "Course completion" from "type"
-    And I fill the moodle form with:
+    And I set the field "type" to "Course completion"
+    And I set the following fields to these values:
       | grade_2 | 0 |
     And I press "Save"
     And I press "Enable access"
index 056bfbd..627ab00 100644 (file)
@@ -46,7 +46,7 @@ class behat_blocks extends behat_base {
      * @param string $blockname
      */
     public function i_add_the_block($blockname) {
-        $steps = new Given('I select "' . $this->escape($blockname) . '" from "bui_addblock"');
+        $steps = new Given('I set the field "bui_addblock" to "' . $this->escape($blockname) . '"');
 
         // If we are running without javascript we need to submit the form.
         if (!$this->running_javascript()) {
index 3812f69..cf0a5c0 100644 (file)
@@ -20,7 +20,7 @@ Feature: Add and configure blocks throughout the site
     And I add the "Comments" block
     And I open the "Comments" blocks action menu
     And I follow "Configure Comments block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Page contexts | Display throughout the entire site |
     And I press "Save changes"
     When I follow "Course 1"
@@ -29,7 +29,7 @@ Feature: Add and configure blocks throughout the site
     And I am on homepage
     And I open the "Comments" blocks action menu
     And I follow "Configure Comments block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Default weight | -10 (first) |
     And I press "Save changes"
     And I follow "Course 1"
index 5f1189d..13e23be 100644 (file)
@@ -33,7 +33,7 @@ Feature: Block appearances
       | Name | Test book name |
       | Description | Test book description |
     And I follow "Test book name"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Chapter title | Book title |
       | Content       | Book content test test |
     And I press "Save changes"
@@ -44,7 +44,7 @@ Feature: Block appearances
     And I add the "Comments" block
     And I open the "Comments" blocks action menu
     And I follow "Configure Comments block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Display on page types | Any page |
     And I press "Save changes"
 
@@ -55,7 +55,7 @@ Feature: Block appearances
     And I follow "Course 1"
     And I open the "Comments" blocks action menu
     And I follow "Configure Comments block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Display on page types | Any course page |
     And I press "Save changes"
     And I follow "Turn editing off"
@@ -67,7 +67,7 @@ Feature: Block appearances
     When I follow "Test book name"
     And I open the "Comments" blocks action menu
     And I follow "Configure Comments block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Visible | No |
     And I press "Save changes"
     And I follow "Turn editing off"
@@ -77,7 +77,7 @@ Feature: Block appearances
     And I follow "Turn editing on"
     And I open the "Comments" blocks action menu
     And I follow "Configure Comments block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Visible | Yes |
       | Region  | Right |
     And I press "Save changes"
index af9bce2..6a8dd03 100644 (file)
@@ -17,7 +17,7 @@ Feature: The context of a block can always be returned to it's original state.
     And I click on "Participants" "link" in the "//li[p/span[contains(normalize-space(string(.)), 'Current course')]]" "xpath_element"
     And I open the "Tags" blocks action menu
     And I follow "Configure Tags block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Display on page types | Any page |
     And I press "Save changes"
     And I follow "Course 1"
@@ -27,7 +27,7 @@ Feature: The context of a block can always be returned to it's original state.
     And I follow "Assignment1"
     And I open the "Tags" blocks action menu
     And I follow "Configure Tags block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Display on page types | Any assignment module page |
     And I press "Save changes"
     And I should see "Tags" in the "Tags" "block"
@@ -43,7 +43,7 @@ Feature: The context of a block can always be returned to it's original state.
     And I should see "Tags" in the "Tags" "block"
     And I open the "Tags" blocks action menu
     And I follow "Configure Tags block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Display on page types | Any page |
     And I press "Save changes"
     And I follow "Course 1"
index 84501bd..4567026 100644 (file)
@@ -14,7 +14,7 @@ Feature: Comment on a blog entry
     And I expand "My profile" node
     And I expand "Blogs" node
     And I follow "Add a new entry"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Entry title | Blog post from user 1 |
       | Blog entry body | User 1 blog post content |
     And I press "Save changes"
@@ -31,10 +31,10 @@ Feature: Comment on a blog entry
     And I follow "Blog post from user 1"
     And I should see "User 1 blog post content"
     And I follow "Comments (0)"
-    When I fill in "content" with "$My own >nasty< \"string\"!"
+    When I set the field "content" to "$My own >nasty< \"string\"!"
     And I follow "Save comment"
     Then I should see "$My own >nasty< \"string\"!"
-    And I fill in "content" with "Another $Nasty <string?>"
+    And I set the field "content" to "Another $Nasty <string?>"
     And I follow "Save comment"
     And I should see "Comments (2)" in the ".comment-link" "css_element"
 
@@ -49,7 +49,7 @@ Feature: Comment on a blog entry
     And I follow "Blog post from user 1"
     And I should see "User 1 blog post content"
     And I follow "Comments (0)"
-    And I fill in "content" with "$My own >nasty< \"string\"!"
+    And I set the field "content" to "$My own >nasty< \"string\"!"
     And I follow "Save comment"
     When I click on ".comment-delete a" "css_element"
     # Waiting for the animation to finish.
@@ -69,6 +69,6 @@ Feature: Comment on a blog entry
     And I follow "Site blogs"
     And I follow "Blog post from user 1"
     When I follow "Comments (0)"
-    And I fill in "content" with "$My own >nasty< \"string\"!"
+    And I set the field "content" to "$My own >nasty< \"string\"!"
     And I follow "Save comment"
     Then I should see "$My own >nasty< \"string\"!"
index 01dc79b..82cd26a 100644 (file)
@@ -54,7 +54,7 @@ class behat_calendar extends behat_base {
         return array(
             new Given('I follow "' . get_string('monththis', 'calendar') . '"'),
             new Given('I click on "' . get_string('newevent', 'calendar') .'" "button"'),
-            new Given('I fill the moodle form with:', $data),
+            new Given('I set the following fields to these values:', $data),
             new Given('I press "' . get_string('savechanges') . '"'),
             new Given('I should see "' . $eventname . '"')
         );
index fc68157..858c826 100644 (file)
@@ -76,7 +76,7 @@ Feature: Perform basic calendar functionality
       | Event title | Really awesome event! |
       | Description | Come join this awesome event, sucka! |
     And I click on "//div[@class='commands']//a[contains(@href, 'edit')]" "xpath_element"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Event title | Mediocre event :( |
       | Description | Wait, this event isn't that great. |
     And I press "Save changes"
index 51ac01c..6639639 100644 (file)
@@ -85,7 +85,6 @@ function cohort_update_cohort($cohort) {
         'context' => context::instance_by_id($cohort->contextid),
         'objectid' => $cohort->id,
     ));
-    $event->add_record_snapshot('cohort', $cohort);
     $event->trigger();
 }
 
index 2021edc..b490ef5 100644 (file)
@@ -18,7 +18,7 @@ Feature: Add cohorts of users
     And I expand "Accounts" node
     And I follow "Cohorts"
     And I press "Add"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Test cohort name |
       | Context | System |
       | Cohort ID | 333 |
@@ -46,13 +46,13 @@ Feature: Add cohorts of users
   @javascript
   Scenario: Add users to a cohort using a bulk user action
     When I follow "Bulk user actions"
-    And I select "Third User" from "Available"
+    And I set the field "Available" to "Third User"
     And I press "Add to selection"
-    And I select "Forth User" from "Available"
+    And I set the field "Available" to "Forth User"
     And I press "Add to selection"
-    And I select "Add to cohort" from "id_action"
+    And I set the field "id_action" to "Add to cohort"
     And I press "Go"
-    And I select "Test cohort name [333]" from "Cohort"
+    And I set the field "Cohort" to "Test cohort name [333]"
     And I press "Add to cohort"
     And I follow "Cohorts"
     Then I should see "2" in the "#cohorts" "css_element"
index fc0c854..adeb88b 100644 (file)
@@ -50,7 +50,7 @@ class behat_cohort extends behat_base {
 
         $steps = array(
             new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" "table_row"'),
-            new Given('I select "' . $this->escape($user) . '" from "' . get_string('potusers', 'cohort') . '"'),
+            new Given('I set the field "' . get_string('potusers', 'cohort') . '" to "' . $this->escape($user) . '"'),
             new Given('I press "' . get_string('add') . '"'),
             new Given('I press "' . get_string('backtocohorts', 'cohort') . '"')
         );
index 98d2982..4a240c3 100644 (file)
@@ -26,7 +26,7 @@ Feature: Allow students to manually mark an activity as complete
     And I follow "Course 1"
     And I turn editing mode on
     And I click on "Edit settings" "link" in the "Administration" "block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Enable completion tracking | Yes |
     And I press "Save changes"
     When I add a "Forum" to section "1" and I fill the form with:
index 52e7a26..fc32ad4 100644 (file)
@@ -30,7 +30,7 @@ Feature: Restrict activity availability through date conditions
   @javascript
   Scenario: Show activity greyed-out to students when available from date is in future
     Given I click on "id_availablefrom_enabled" "checkbox"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Assignment name | Test assignment 1 |
       | Description | This assignment is restricted by date |
       | assignsubmission_onlinetext_enabled | 1 |
@@ -50,7 +50,7 @@ Feature: Restrict activity availability through date conditions
   @javascript
   Scenario: Show activity hidden to students when available until date is in past
     Given I click on "id_availableuntil_enabled" "checkbox"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Assignment name | Test assignment 2 |
       | Description | This assignment is restricted by date |
       | assignsubmission_onlinetext_enabled | 1 |
index 72154be..ac2c410 100644 (file)
@@ -33,7 +33,7 @@ Feature: Restrict activity availability through grade conditions
     And I add a "Page" to section "2"
     And I expand all fieldsets
     And I click on "id_availablefrom_enabled" "checkbox"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Test page name |
       | Description | Restricted page, till grades in Grade assignment is at least 20% |
       | Page content | Test page contents |
@@ -48,7 +48,7 @@ Feature: Restrict activity availability through grade conditions
     And "Test page name" activity should be hidden
     And I follow "Grade assignment"
     And I press "Add submission"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Online text | I'm the student submission |
     And I press "Save changes"
     And I should see "Submitted for grading"
@@ -58,7 +58,7 @@ Feature: Restrict activity availability through grade conditions
     And I follow "Grade assignment"
     And I follow "View/grade all submissions"
     And I click on "Grade Student First" "link" in the "Student First" "table_row"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Grade | 21 |
     And I press "Save changes"
     And I log out
index 5068801..3bf2d79 100644 (file)
@@ -28,7 +28,7 @@ Feature: Restrict sections availability through completion or grade conditions
     And I follow "Course 1"
     And I turn editing mode on
     And I click on "Edit settings" "link" in the "Administration" "block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Enable completion tracking | Yes |
     And I press "Save changes"
     And I add a "Label" to section "1" and I fill the form with:
@@ -76,7 +76,7 @@ Feature: Restrict sections availability through completion or grade conditions
     And "Test page name" activity should be hidden
     And I follow "Grade assignment"
     And I press "Add submission"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Online text | I'm the student submission |
     And I press "Save changes"
     And I should see "Submitted for grading"
@@ -86,7 +86,7 @@ Feature: Restrict sections availability through completion or grade conditions
     And I follow "Grade assignment"
     And I follow "View/grade all submissions"
     And I click on "Grade Student First" "link" in the "Student First" "table_row"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Grade | 21 |
     And I press "Save changes"
     And I log out
index edc7d87..9d35994 100644 (file)
@@ -585,7 +585,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         $section = 1;
         while ($section <= $course->numsections) {
             $thissection = $modinfo->get_section_info($section);
-            $showsection = $thissection->uservisible or !$course->hiddensections;
+            $showsection = ($thissection->uservisible or !$course->hiddensections);
             if (($showsection) && ($section != $displaysection) && ($url = course_get_url($course, $section))) {
                 $sectionmenu[$url->out(false)] = get_section_name($course, $section);
             }
index 2fde998..16b9d55 100644 (file)
@@ -22,7 +22,7 @@ Feature: Toggle activities groups mode from the course page
       | Forum name | Test forum name |
       | Description | Test forum description |
     And I click on "Edit settings" "link" in the "Administration" "block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Group mode | No groups |
       | Force group mode | No |
     When I press "Save changes"
index de34b84..a452a21 100644 (file)
@@ -32,9 +32,9 @@ Feature: Add activities to courses
     And I follow "Test name"
     And I click on "Edit settings" "link" in the "Administration" "block"
     And I expand all fieldsets
-    And the "Name" field should match "Test name" value
-    And the "Entries required for completion" field should match "9" value
-    And the "Allow comments on entries" field should match "Yes" value
+    And the field "Name" matches value "Test name"
+    And the field "Entries required for completion" matches value "9"
+    And the field "Allow comments on entries" matches value "Yes"
 
   @javascript
   Scenario: Add an activity without the required fields
@@ -54,12 +54,12 @@ Feature: Add activities to courses
     And I should see "Add an activity to section 'Topic 3'"
     And I add a "Label" to section "2"
     And I should see "Adding a new Label to Topic 2"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Label text | I'm a label |
     And I press "Save and return to course"
     And I add a "Database" to section "3"
     And I should see "Adding a new Database to Topic 3"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Name | Test database name |
       | Description | Test database description |
     And I press "Save and return to course"
index 08d1e2d..e1751e0 100644 (file)
@@ -102,10 +102,10 @@ class behat_course extends behat_base {
             // Adding a forced wait until editors are loaded as otherwise selenium sometimes tries clicks on the
             // format field when the editor is being rendered and the click misses the field coordinates.
             $steps[] = new Given('I expand all fieldsets');
-            $steps[] = new Given('I select "' . $formatvalue . '" from "' . $formatfield . '"');
-            $steps[] = new Given('I fill the moodle form with:', $table);
+            $steps[] = new Given('I set the field "' . $formatfield . '" to "' . $formatvalue . '"');
+            $steps[] = new Given('I set the following fields to these values:', $table);
         } else {
-            $steps[] = new Given('I fill the moodle form with:', $table);
+            $steps[] = new Given('I set the following fields to these values:', $table);
         }
 
         $steps[] = new Given('I press "' . get_string('savechanges') . '"');
@@ -141,7 +141,7 @@ class behat_course extends behat_base {
 
         return array(
             new Given('I add a "' . $this->escape($activity) . '" to section "' . $this->escape($section) . '"'),
-            new Given('I fill the moodle form with:', $data),
+            new Given('I set the following fields to these values:', $data),
             new Given('I press "' . get_string('savechangesandreturntocourse') . '"')
         );
     }
@@ -288,7 +288,7 @@ class behat_course extends behat_base {
 
         return array(
             new Given('I edit the section "' . $sectionnumber . '"'),
-            new Given('I fill the moodle form with:', $data),
+            new Given('I set the following fields to these values:', $data),
             new Given('I press "' . get_string('savechanges') . '"')
         );
     }
@@ -577,7 +577,7 @@ class behat_course extends behat_base {
         $activity = $this->escape($activityname);
         return array(
             new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $activity .'" activity'),
-            new Given('I fill in "title" with "' . $this->escape($newactivityname) . chr(10) . '"')
+            new Given('I set the field "title" to "' . $this->escape($newactivityname) . chr(10) . '"')
         );
     }
 
@@ -783,7 +783,7 @@ class behat_course extends behat_base {
             $steps[] = new Given('I press "' . get_string('continue') .'"');
             $steps[] = new Given('I press "' . get_string('duplicatecontedit') . '"');
         }
-        $steps[] = new Given('I fill the moodle form with:', $data);
+        $steps[] = new Given('I set the following fields to these values:', $data);
         $steps[] = new Given('I press "' . get_string('savechangesandreturntocourse') . '"');
         return $steps;
     }
@@ -1185,7 +1185,7 @@ class behat_course extends behat_base {
     public function i_move_category_to_top_level_in_the_management_interface($name) {
         $this->i_select_category_in_the_management_interface($name);
         return array(
-            new Given('I select "' .  coursecat::get(0)->get_formatted_name() . '" from "menumovecategoriesto"'),
+            new Given('I set the field "menumovecategoriesto" to "' .  coursecat::get(0)->get_formatted_name() . '"'),
             new Given('I press "bulkmovecategories"'),
         );
     }
index baed279..18a0b09 100644 (file)
@@ -33,7 +33,7 @@ Feature: Test category management actions
     # Redirect
     And I should see "Edit category settings"
     And I should see "Cat 1"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Category name | Category 1 (edited) |
       | Category ID number | CAT1e |
     And I press "Save changes"
@@ -88,7 +88,7 @@ Feature: Test category management actions
     And I click on "delete" action for "Cat 3" in management category listing
     # Redirect
     And I should see "Delete category: Cat 3"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | What to do | Move contents to another category |
       | Move into  | Cat 1                             |
     And I press "Delete"
@@ -187,7 +187,7 @@ Feature: Test category management actions
     And I click on "Create new category" "link" in the ".category-listing-actions" "css_element"
     # Redirect.
     And I should see "Add new category"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Category name | Test category 2 |
       | Category ID number | TC2 |
     And I press "Create category"
@@ -199,7 +199,7 @@ Feature: Test category management actions
     And I click on "createnewsubcategory" action for "Test category 2" in management category listing
     # Redirect
     And I should see "Add new category"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Category name | Test category 3 |
       | Category ID number | TC3 |
     And I press "Create category"
@@ -225,7 +225,7 @@ Feature: Test category management actions
     And I should see "Cat 3" in the "#category-listing ul.ml" "css_element"
     And I select category "Cat 2" in the management interface
     And I select category "Cat 3" in the management interface
-    And I select "Cat 1" from "menumovecategoriesto"
+    And I set the field "menumovecategoriesto" to "Cat 1"
     When I press "bulkmovecategories"
     # Redirect
     And I click on category "Cat 1" in the management interface
@@ -257,7 +257,7 @@ Feature: Test category management actions
     And the "movecategoriesto" "select" should be disabled
     And the "resortcategoriesby" "select" should be disabled
     And the "resortcoursesby" "select" should be disabled
-    When I select "allcategories" from "selectsortby"
+    When I set the field "selectsortby" to "allcategories"
     Then the "resortcategoriesby" "select" should be enabled
     And the "resortcoursesby" "select" should be enabled
     And the "movecategoriesto" "select" should be disabled
@@ -265,7 +265,7 @@ Feature: Test category management actions
     And the "movecategoriesto" "select" should be enabled
     And the "resortcategoriesby" "select" should be enabled
     And the "resortcoursesby" "select" should be enabled
-    And I select "selectedcategories" from "selectsortby"
+    And I set the field "selectsortby" to "selectedcategories"
     And the "movecategoriesto" "select" should be enabled
     And the "resortcategoriesby" "select" should be enabled
     And the "resortcoursesby" "select" should be enabled
index 5795413..06deffd 100644 (file)
@@ -15,8 +15,8 @@ Feature: Test we can resort categories in the management interface.
     And I log in as "admin"
     And I go to the courses management page
     And I should see the "Course categories" management page
-    And I select "All categories" from "menuselectsortby"
-    And I select <sortby> from "menuresortcategoriesby"
+    And I set the field "menuselectsortby" to "All categories"
+    And I set the field "menuresortcategoriesby" to <sortby>
     And I press "Sort"
     # Redirect.
     And I should see the "Course categories" management page
@@ -42,8 +42,8 @@ Feature: Test we can resort categories in the management interface.
     And I click on "Test category" "link"
     # Redirect.
     And I should see the "Course categories and courses" management page
-    And I select "This category" from "menuselectsortby"
-    And I select <sortby> from "menuresortcategoriesby"
+    And I set the field "menuselectsortby" to "This category"
+    And I set the field "menuresortcategoriesby" to <sortby>
     And I press "Sort"
     # Redirect.
     And I should see the "Course categories and courses" management page
index d7a96a1..2c9d842 100644 (file)
@@ -245,8 +245,8 @@ Feature: Course category management interface performs as expected
     And I log in as "admin"
     And I go to the courses management page
     And I should see the "Course categories" management page
-    And I select "All categories" from "menuselectsortby"
-    And I select <sortby> from "menuresortcategoriesby"
+    And I set the field "menuselectsortby" to "All categories"
+    And I set the field "menuresortcategoriesby" to <sortby>
     And I press "Sort"
     # Redirect.
     And I should see the "Course categories" management page
index 90a911e..60cd976 100644 (file)
@@ -58,7 +58,7 @@ Feature: Course activity controls works as expected
     And I click on "Edit settings" "link" in the "Test forum name 1" activity
     And I should see "Updating Forum"
     And I should see "Display description on course page"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Forum name | Just to check that I can edit the name |
       | Description | Just to check that I can edit the description |
       | Display description on course page | 1 |
index 30999a9..f45ca9c 100644 (file)
@@ -18,7 +18,7 @@ Feature: Test we can both create and delete a course.
     And I should see "Cat 1" in the "#category-listing" "css_element"
     And I should see "No courses in this category" in the "#course-listing" "css_element"
     And I click on "Create new course" "link" in the ".course-listing-actions" "css_element"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Course full name | Test course: create a course |
       | Course short name | TCCAC |
       | Course ID number | TC3401 |
index f2a8115..68e2ee8 100644 (file)
@@ -18,7 +18,7 @@ Feature: Edit course settings
     And I log in as "teacher1"
     And I follow "Course 1"
     When I click on "Edit settings" "link" in the "Administration" "block"
-    And I fill the moodle form with:
+    And I set the following fields to these values:
       | Course full name | Edited course fullname |
       | Course short name | Edited course shortname |
       | Course summary | Edited course summary |