Merge branch 'MDL-52965_master' of git://github.com/dmonllao/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Mon, 29 Feb 2016 07:20:44 +0000 (15:20 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Mon, 29 Feb 2016 07:20:44 +0000 (15:20 +0800)
303 files changed:
CONTRIBUTING.txt [new file with mode: 0644]
INSTALL.txt [new file with mode: 0644]
PULL_REQUEST_TEMPLATE.txt [new file with mode: 0644]
README.txt
TRADEMARK.txt
admin/renderer.php
admin/roles/classes/capability_table_with_risks.php
admin/settings/plugins.php
admin/settings/subsystems.php
admin/tool/log/store/legacy/classes/log/store.php
admin/tool/task/scheduledtasks.php
admin/tool/templatelibrary/tests/externallib_test.php
admin/user/user_bulk_cohortadd.php
badges/action.php
blocks/globalsearch/block_globalsearch.php [new file with mode: 0644]
blocks/globalsearch/db/access.php [new file with mode: 0644]
blocks/globalsearch/lang/en/block_globalsearch.php [new file with mode: 0644]
blocks/globalsearch/version.php [new file with mode: 0644]
blocks/navigation/renderer.php
cache/README.md
cache/classes/definition.php
cache/classes/dummystore.php
cache/classes/loaders.php
cache/classes/store.php
cache/forms.php
cache/locallib.php
cache/stores/file/lib.php
cache/stores/memcache/lib.php
cache/stores/memcached/lib.php
cache/stores/mongodb/lib.php
cache/tests/cache_test.php
cache/tests/fixtures/lib.php
cache/upgrade.txt
course/delete.php
course/editsection_form.php
course/tests/behat/behat_course.php
enrol/cohort/edit.php [deleted file]
enrol/cohort/edit_form.php [deleted file]
enrol/cohort/lib.php
enrol/editinstance.php [moved from enrol/guest/edit.php with 59% similarity]
enrol/editinstance_form.php [new file with mode: 0644]
enrol/guest/classes/enrol_guest_edit_form.php [deleted file]
enrol/guest/lib.php
enrol/guest/locallib.php
enrol/instances.php
enrol/manual/edit.php [deleted file]
enrol/manual/edit_form.php [deleted file]
enrol/manual/lib.php
enrol/meta/addinstance.php [deleted file]
enrol/meta/addinstance_form.php [deleted file]
enrol/meta/lib.php
enrol/meta/tests/behat/enrol_meta.feature
enrol/meta/tests/plugin_test.php
enrol/mnet/addinstance.php [deleted file]
enrol/mnet/addinstance_form.php [deleted file]
enrol/mnet/lib.php
enrol/paypal/edit.php [deleted file]
enrol/paypal/edit_form.php [deleted file]
enrol/paypal/lib.php
enrol/self/edit.php [deleted file]
enrol/self/edit_form.php [deleted file]
enrol/self/lib.php
enrol/upgrade.txt
grade/edit/tree/index.php
grade/export/lib.php
grade/grading/form/guide/guideeditor.php
grade/grading/form/guide/lang/en/gradingform_guide.php
grade/grading/form/guide/lib.php
grade/grading/form/rubric/renderer.php
grade/lib.php
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js
grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js
grade/report/outcomes/index.php
grade/report/overview/index.php
grade/report/singleview/index.php
grade/report/singleview/tests/behat/bulk_insert_grades.feature
grade/report/singleview/tests/behat/singleview.feature
grade/report/singleview/tests/screen_test.php
grade/report/user/index.php
grade/report/user/lib.php
grade/report/user/tests/behat/view_usereport.feature
grade/tests/behat/grade_UI_settings.feature
grade/tests/behat/grade_override_letter.feature
grade/tests/behat/grade_point_maximum.feature
grade/tests/behat/grade_scales_aggregation.feature
grade/tests/behat/grade_single_item_scales.feature
grade/tests/behat/grade_to_pass.feature
install/lang/ca/admin.php
install/lang/pt/install.php
lang/en/admin.php
lang/en/cache.php
lang/en/error.php
lang/en/grades.php
lang/en/moodle.php
lang/en/role.php
lang/en/search.php
lib/adminlib.php
lib/amd/build/notification.min.js
lib/amd/build/search-input.min.js [new file with mode: 0644]
lib/amd/src/notification.js
lib/amd/src/search-input.js [new file with mode: 0644]
lib/behat/form_field/behat_form_select.php
lib/classes/component.php
lib/classes/event/search_indexed.php [new file with mode: 0644]
lib/classes/notification.php [new file with mode: 0644]
lib/classes/output/notification.php
lib/classes/plugin_manager.php
lib/classes/plugininfo/search.php [new file with mode: 0644]
lib/classes/progress/display.php
lib/classes/session/manager.php
lib/classes/task/search_task.php [new file with mode: 0644]
lib/db/access.php
lib/db/caches.php
lib/db/events.php
lib/db/services.php
lib/db/tasks.php
lib/deprecatedlib.php
lib/editor/atto/lib.php
lib/editor/atto/plugins/image/tests/behat/image.feature
lib/editor/atto/tests/behat/customtoolbar.feature [new file with mode: 0644]
lib/editor/atto/tests/fixtures/custom_toolbar_example.php [new file with mode: 0644]
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
lib/editor/atto/yui/src/editor/js/selection.js
lib/editorlib.php
lib/enrollib.php
lib/external/externallib.php
lib/flowplayer/LICENSE.txt [new file with mode: 0644]
lib/flowplayer/readme_moodle.txt
lib/form/advcheckbox.php
lib/form/text.php
lib/form/url.php
lib/formslib.php
lib/gradelib.php
lib/medialib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/pear/HTML/QuickForm/element.php
lib/pear/README_MOODLE.txt
lib/phpunit/classes/util.php
lib/templates/notification_error.mustache [moved from lib/templates/notification_redirect.mustache with 57% similarity]
lib/templates/notification_info.mustache [moved from lib/templates/notification_message.mustache with 57% similarity]
lib/templates/notification_success.mustache
lib/templates/notification_warning.mustache [moved from lib/templates/notification_problem.mustache with 57% similarity]
lib/tests/behat/behat_navigation.php
lib/tests/component_test.php
lib/tests/formslib_test.php
lib/tests/notification_test.php [new file with mode: 0644]
lib/tests/session_manager_test.php
lib/tests/sessionlib_test.php
lib/upgrade.txt
lib/weblib.php
message/lib.php
mod/assign/classes/search/activity.php [new file with mode: 0644]
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/comments/tests/comments_test.php [new file with mode: 0644]
mod/assign/feedback/editpdf/locallib.php
mod/assign/feedback/editpdf/tests/editpdf_test.php
mod/assign/feedback/file/locallib.php
mod/assign/feedback/file/tests/file_test.php [new file with mode: 0644]
mod/assign/feedbackplugin.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/tests/locallib_test.php
mod/assign/upgrade.txt
mod/book/classes/search/activity.php [new file with mode: 0644]
mod/book/lang/en/book.php
mod/chat/classes/search/activity.php [new file with mode: 0644]
mod/chat/lang/en/chat.php
mod/choice/classes/search/activity.php [new file with mode: 0644]
mod/choice/lang/en/choice.php
mod/feedback/analysis.php
mod/feedback/classes/search/activity.php [new file with mode: 0644]
mod/feedback/complete.php
mod/feedback/complete_guest.php
mod/feedback/edit.php
mod/feedback/edit_form.php
mod/feedback/item/label/label_form.php
mod/feedback/item/multichoice/lib.php
mod/feedback/item/multichoice/multichoice_form.php
mod/feedback/item/multichoicerated/lib.php
mod/feedback/item/multichoicerated/multichoicerated_form.php
mod/feedback/item/textarea/lib.php
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/feedback/tests/behat/behat_mod_feedback.php [new file with mode: 0644]
mod/feedback/tests/behat/question_types.feature [new file with mode: 0644]
mod/feedback/tests/behat/show_nonrespondents.feature
mod/folder/classes/search/activity.php [new file with mode: 0644]
mod/folder/lang/en/folder.php
mod/forum/classes/search/activity.php [new file with mode: 0644]
mod/forum/classes/search/post.php [new file with mode: 0644]
mod/forum/index.php
mod/forum/lang/en/forum.php
mod/forum/maildigest.php
mod/forum/post.php
mod/forum/subscribe.php
mod/forum/tests/behat/discussion_subscriptions.feature
mod/forum/tests/behat/forum_subscriptions.feature
mod/forum/tests/behat/forum_subscriptions_default.feature
mod/forum/tests/search_test.php [new file with mode: 0644]
mod/glossary/classes/external.php
mod/glossary/classes/search/activity.php [new file with mode: 0644]
mod/glossary/classes/search/entry.php [new file with mode: 0644]
mod/glossary/lang/en/glossary.php
mod/glossary/lib.php
mod/glossary/tests/search_test.php [new file with mode: 0644]
mod/imscp/classes/search/activity.php [new file with mode: 0644]
mod/imscp/lang/en/imscp.php
mod/imscp/view.php
mod/label/classes/search/activity.php [new file with mode: 0644]
mod/label/lang/en/label.php
mod/lesson/classes/search/activity.php [new file with mode: 0644]
mod/lesson/lang/en/lesson.php
mod/lesson/pagetypes/truefalse.php
mod/lesson/styles.css
mod/lti/classes/search/activity.php [new file with mode: 0644]
mod/lti/lang/en/lti.php
mod/page/classes/search/activity.php [new file with mode: 0644]
mod/page/lang/en/page.php
mod/quiz/attemptlib.php
mod/quiz/classes/output/edit_renderer.php
mod/quiz/classes/search/activity.php [new file with mode: 0644]
mod/quiz/lang/en/quiz.php
mod/quiz/locallib.php
mod/quiz/report/grading/report.php
mod/quiz/report/overview/report.php
mod/quiz/report/statistics/statistics_table.php
mod/quiz/upgrade.txt
mod/resource/classes/search/activity.php [new file with mode: 0644]
mod/resource/lang/en/resource.php
mod/scorm/classes/search/activity.php [new file with mode: 0644]
mod/scorm/lang/en/scorm.php
mod/survey/classes/search/activity.php [new file with mode: 0644]
mod/survey/lang/en/survey.php
mod/url/classes/search/activity.php [new file with mode: 0644]
mod/url/lang/en/url.php
mod/wiki/classes/search/activity.php [new file with mode: 0644]
mod/wiki/lang/en/wiki.php
mod/workshop/classes/search/activity.php [new file with mode: 0644]
mod/workshop/lang/en/workshop.php
my/tests/behat/reset_all_pages.feature
phpunit.xml.dist
question/classes/engine/variants/least_used_strategy.php
report/search/classes/output/form.php [new file with mode: 0644]
report/search/classes/output/renderer.php [new file with mode: 0644]
report/search/index.php [new file with mode: 0644]
report/search/lang/en/report_search.php [new file with mode: 0644]
report/search/settings.php [new file with mode: 0644]
report/search/version.php [new file with mode: 0644]
search/classes/area/base.php [new file with mode: 0644]
search/classes/area/base_activity.php [new file with mode: 0644]
search/classes/area/base_mod.php [new file with mode: 0644]
search/classes/document.php [new file with mode: 0644]
search/classes/document_factory.php [new file with mode: 0644]
search/classes/engine.php [new file with mode: 0644]
search/classes/engine_exception.php [new file with mode: 0644]
search/classes/manager.php [new file with mode: 0644]
search/classes/observer.php [new file with mode: 0644]
search/classes/output/form/search.php [new file with mode: 0644]
search/classes/output/renderer.php [new file with mode: 0644]
search/engine/solr/classes/document.php [new file with mode: 0644]
search/engine/solr/classes/engine.php [new file with mode: 0644]
search/engine/solr/classes/schema.php [new file with mode: 0644]
search/engine/solr/lang/en/search_solr.php [new file with mode: 0644]
search/engine/solr/settings.php [new file with mode: 0644]
search/engine/solr/setup_schema.php [new file with mode: 0644]
search/engine/solr/tests/engine_test.php [new file with mode: 0644]
search/engine/solr/version.php [new file with mode: 0644]
search/index.php [new file with mode: 0644]
search/templates/result.mustache [new file with mode: 0644]
search/tests/engine_test.php [new file with mode: 0644]
search/tests/fixtures/mock_search_area.php [new file with mode: 0644]
search/tests/fixtures/mock_search_engine.php [new file with mode: 0644]
search/tests/fixtures/testable_core_search.php [new file with mode: 0644]
search/tests/manager_test.php [new file with mode: 0644]
tag/tests/behat/flag_tags.feature
theme/base/config.php
theme/base/style/core.css
theme/base/style/search.css [new file with mode: 0644]
theme/base/templates/core/notification_error.mustache [moved from theme/base/templates/core/notification_problem.mustache with 100% similarity]
theme/base/templates/core/notification_info.mustache [moved from theme/base/templates/core/notification_redirect.mustache with 100% similarity]
theme/base/templates/core/notification_warning.mustache [moved from theme/base/templates/core/notification_message.mustache with 100% similarity]
theme/bootstrapbase/layout/columns1.php
theme/bootstrapbase/layout/columns2.php
theme/bootstrapbase/layout/columns3.php
theme/bootstrapbase/less/moodle.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/search.less [new file with mode: 0644]
theme/bootstrapbase/style/moodle.css
theme/clean/layout/columns1.php
theme/clean/layout/columns2.php
theme/clean/layout/columns3.php
theme/upgrade.txt
user/addnote.php
user/emailupdate.php
user/view.php
version.php

diff --git a/CONTRIBUTING.txt b/CONTRIBUTING.txt
new file mode 100644 (file)
index 0000000..cfadb71
--- /dev/null
@@ -0,0 +1,56 @@
+CONTRIBUTING TO MOODLE
+======================
+
+Moodle is made by people like you. We are members of a big worldwide community
+of developers, designers, teachers, testers, translators and other users. We
+work in universities, schools, companies and other places. You are very welcome
+to join us and contribute to the project.
+
+See <https://docs.moodle.org/dev/Contributing_to_Moodle> for the many ways you
+can help, not only with coding.
+
+Moodle is open to community contributions to core, though all code must go
+through peer-review, automated behaviour testing, continuous integration and
+human post-integration checks.
+
+Pull requests
+-------------
+
+Please do not open pull requests via Github. The repository there is just a
+mirror of the official repository at <https://git.moodle.org>. Issues are
+reported and patches provided via <https://tracker.moodle.org>. See below for
+more information.
+
+Moodle core bug fixes and new features
+--------------------------------------
+
+During the years of intensive development, a mature process of including
+submitted patches has evolved.
+
+* Every bug fix or new feature must have a tracker issue.
+* You publish the branch implementing the fix or new feature in your public
+  clone of the moodle.git repository (typically on Github).
+* Your patch is peer-reviewed, discussed, integrated, tested and then released
+  as a part of moodle.git.
+* New features are developed on the master branch. Bug fixes are also
+  backported to currently supported maintenance (stable) branches.
+
+For further details, see <https://docs.moodle.org/dev/Process>.
+
+Moodle plugins
+--------------
+
+Moodle has a framework for additional plugins to extend its functionality. We
+have a Moodle plugins directory <https://moodle.org/plugins/> where you can
+register and maintain your plugin. Plugins hosted in the plugins directory can
+be easily installed and updated via the Moodle administration interface.
+
+* You are expected to have a public source code repository with your plugin
+  code.
+* After registering your plugin in the plugins directory it is reviewed before
+  being published.
+* You are expected to continuously release updated versions of the plugin via
+  the plugins directory. We do not pull from your code repository; you must do
+  it explicitly.
+
+For further details, see <https://docs.moodle.org/dev/Plugin_contribution>.
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644 (file)
index 0000000..8f8689b
--- /dev/null
@@ -0,0 +1,20 @@
+MOODLE INSTALLATION
+===================
+
+Here is a short summary of the installation process (which can take just a few
+minutes):
+
+1. Move the Moodle files into your web directory.
+
+2. Create a single database for Moodle to store all its tables in (or choose an
+   existing database).
+
+3. Visit your Moodle site with a browser. You should be taken to the
+   install.php script, which will lead you through creating a config.php file
+   and then setting up Moodle, creating an admin account etc.
+
+4. Set up a cron task to call the file admin/cron.php every minute.
+
+For more information, see <https://docs.moodle.org/en/Installing_Moodle>.
+
+Good luck and have fun!
diff --git a/PULL_REQUEST_TEMPLATE.txt b/PULL_REQUEST_TEMPLATE.txt
new file mode 100644 (file)
index 0000000..c497344
--- /dev/null
@@ -0,0 +1,7 @@
+*** PLEASE DO NOT OPEN PULL REQUESTS VIA GITHUB ***
+
+The moodle.git repository at Github is just a mirror of the official repository. We do not accept pull requests at Github.
+
+See CONTRIBUTING.txt guidelines for how to contribute patches for Moodle. Thank you.
+
+--
index b1017df..729dbe4 100644 (file)
@@ -1,29 +1,28 @@
-QUICK INSTALL
-=============
+                                 .-..-.
+   _____                         | || |
+  /____/-.---_  .---.  .---.  .-.| || | .---.
+  | |  _   _  |/  _  \/  _  \/  _  || |/  __ \
+  * | | | | | || |_| || |_| || |_| || || |___/
+    |_| |_| |_|\_____/\_____/\_____||_|\_____)
 
-For the impatient, here is a basic outline of the
-installation process, which normally takes me only
-a few minutes:
+Moodle - the world's open source learning platform
 
-1) Move the Moodle files into your web directory.
+Moodle <https://moodle.org> is a learning platform designed to provide
+educators, administrators and learners with a single robust, secure and
+integrated system to create personalised learning environments.
 
-2) Create a single database for Moodle to store all
-   its tables in (or choose an existing database).
+You can download Moodle <https://download.moodle.org> and run it on your own
+web server, ask one of our Moodle Partners <https://moodle.com/partners/> to
+assist you, or have a MoodleCloud site <https://moodle.com/cloud/> set up for
+you.
 
-3) Visit your Moodle site with a browser, you should
-   be taken to the install.php script, which will lead
-   you through creating a config.php file and then
-   setting up Moodle, creating an admin account etc.
+Moodle is widely used around the world by universities, schools, companies and
+all manner of organisations and individuals.
 
-4) Set up a cron task to call the file admin/cron.php
-   every five minutes or so.
+Moodle is provided freely as open source software, under the GNU General Public
+License <https://docs.moodle.org/dev/License>.
 
+Moodle is written in PHP and JavaScript and uses an SQL database for storing
+the data.
 
-For more information, see the INSTALL DOCUMENTATION:
-
-   http://docs.moodle.org/en/Installing_Moodle
-
-
-Good luck and have fun!
-Martin Dougiamas, Lead Developer
-
+See <https://docs.moodle.org> for details of Moodle's many features.
index d73a076..f1e3f15 100644 (file)
@@ -1,34 +1,48 @@
-------------------------
-Moodle Trademark License
-------------------------
-
-The name Moodle™ is a registered trademark of the Moodle Trust.
-
-A key part of the business model that allows us to produce and
-distribute Moodle as completely Free open source software is that
-we restrict the commercial use of the Moodle trademark to those
-who have contracted to support Moodle development (Moodle Partners).
-
-If you are intending to use the name (and/or logo) to advertise
-generic Moodle™ services (eg Moodle Hosting, Moodle Support,
-Moodle Certification, Moodle Training, Moodle Consulting,
-Moodle Customisation, Moodle Courseware Development, Moodle
-Theme design, Moodle Integrations, Moodle Installations, etc)
-or as the name of a software package, then you must seek
-direct permission in writing from the Moodle Trust via the
-moodle.com helpdesk, in accordance with normal trademark
-restrictions.
-
-There are no restrictions on how you use the name in other
-contexts (for example, if you use Moodle just to provide
-courses then you can use the name freely to refer to it.)
-
-If you aren't sure of a particular case, please ask us via
-http://moodle.com/helpdesk: we'll be happy to either provide
-you with official permission in writing or help you fix
-your wording.
-
-Martin Dougiamas
-Executive Director
-Moodle Trust
-http://moodle.com
+MOODLE TRADEMARKS POLICY
+========================
+
+The word "Moodle" is trademarked in many countries around the world. The word
+was originally an acronym: Modular Object-Oriented Dynamic Learning
+Environment. The trademark is owned by Martin Dougiamas, Founder and Director
+of Moodle.
+
+The law obligates trademark owners to police their marks and prevent the use of
+confusingly similar names by third parties. Through this policy we’d like to
+make it clear how Moodle-related projects, organisations, and people can use
+the Moodle trademark. We’d also like to be clear about how use of the word is
+restricted when used to promote commercial Moodle services. We do this to
+protect the very business model that allows us to continue developing Moodle
+for you.
+
+Allowed uses
+------------
+
+The following uses don’t require any permission at all:
+
+* Referring to the software or the Moodle project.
+* Describing your own Moodle implementation (including within corporate
+  settings).
+* Describing a Moodle-based community hub.
+* Describing some software you’ve made that integrates with Moodle
+  (eg a Moodle integration feature on another system).
+
+Restricted uses
+---------------
+
+The following uses are generally prohibited without explicit and direct
+permission being granted to you by Moodle Pty Ltd. We do this to protect the
+Moodle project from software and sites which could confuse people. Please
+contact us to ask for permission in writing.
+
+* You can’t use "Moodle" in the name of your software (including Mobile apps)
+* You can’t use "Moodle" in your company name
+* You can’t use "Moodle" in your domain name
+* You can’t use "Moodle" in advertising-related keywords (such as Adsense)
+* You can’t use "Moodle" to describe services around Moodle (such as hosting,
+  training, support, consulting, course creation services, theme development,
+  customisation, installation, integration and certification). This applies
+  even if you do not charge for the services. Note that usually only Moodle
+  Partners have this permission.
+
+For information about the Moodle Partner Certification Mark as well as for how
+to contact us, please see <https://moodle.com/trademarks/>.
index 7c0dfe2..7895c31 100644 (file)
@@ -287,6 +287,7 @@ class core_admin_renderer extends plugin_renderer_base {
 
         $output .= $this->header();
         $output .= $this->maturity_info($maturity);
+        $output .= $this->legacy_log_store_writing_error();
         $output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
         $output .= $this->insecure_dataroot_warning($insecuredataroot);
         $output .= $this->display_errors_warning($errorsdisplayed);
@@ -1954,4 +1955,19 @@ class core_admin_renderer extends plugin_renderer_base {
 
         return $output;
     }
+
+    /**
+     * Check to see if writing to the deprecated legacy log store is enabled.
+     *
+     * @return string An error message if writing to the legacy log store is enabled.
+     */
+    protected function legacy_log_store_writing_error() {
+        $enabled = get_config('logstore_legacy', 'loglegacy');
+        $plugins = explode(',', get_config('tool_log', 'enabled_stores'));
+        $enabled = $enabled && in_array('logstore_legacy', $plugins);
+
+        if ($enabled) {
+            return $this->warning(get_string('legacylogginginuse'));
+        }
+    }
 }
index acf7257..96addb5 100644 (file)
@@ -35,7 +35,6 @@ abstract class core_role_capability_table_with_risks extends core_role_capabilit
     protected $allpermissions; // We don't need perms ourselves, but all our subclasses do.
     protected $strperms; // Language string cache.
     protected $risksurl; // URL in moodledocs about risks.
-    protected $riskicons = array(); // Cache to avoid regenerating the HTML for each risk icon.
     /** @var array The capabilities to highlight as default/inherited. */
     protected $parentpermissions;
     protected $displaypermissions;
@@ -183,12 +182,12 @@ abstract class core_role_capability_table_with_risks extends core_role_capabilit
      */
     public function get_risk_icon($type) {
         global $OUTPUT;
-        if (!isset($this->riskicons[$type])) {
-            $iconurl = $OUTPUT->pix_url('i/' . str_replace('risk', 'risk_', $type));
-            $text = '<img src="' . $iconurl . '" alt="' . get_string($type . 'short', 'admin') . '" />';
-            $action = new popup_action('click', $this->risksurl, 'docspopup');
-            $this->riskicons[$type] = $OUTPUT->action_link($this->risksurl, $text, $action, array('title'=>get_string($type, 'admin')));
-        }
-        return $this->riskicons[$type];
+
+        $iconurl = $OUTPUT->pix_url('i/' . str_replace('risk', 'risk_', $type));
+        $text = '<img src="' . $iconurl . '" alt="' . get_string($type . 'short', 'admin') . '" />';
+        $action = new popup_action('click', $this->risksurl, 'docspopup');
+        $riskicon = $OUTPUT->action_link($this->risksurl, $text, $action, array('title'=>get_string($type, 'admin')));
+
+        return $riskicon;
     }
 }
index 9ed5af8..1e3322b 100644 (file)
@@ -457,6 +457,49 @@ foreach ($pages as $page) {
     $ADMIN->add('reportplugins', $page);
 }
 
+if ($hassiteconfig) {
+    // Global Search engine plugins.
+    $ADMIN->add('modules', new admin_category('searchplugins', new lang_string('search', 'admin')));
+    $temp = new admin_settingpage('manageglobalsearch', new lang_string('globalsearchmanage', 'admin'));
+
+    $pages = array();
+    $engines = array();
+    foreach (core_component::get_plugin_list('search') as $engine => $plugindir) {
+        $engines[$engine] = new lang_string('pluginname', 'search_' . $engine);
+        $settingspath = "$plugindir/settings.php";
+        if (file_exists($settingspath)) {
+            $settings = new admin_settingpage('search' . $engine,
+                    new lang_string('pluginname', 'search_' . $engine), 'moodle/site:config');
+            include($settingspath);
+            if ($settings) {
+                $pages[] = $settings;
+            }
+        }
+    }
+
+    // Setup status.
+    $temp->add(new admin_setting_searchsetupinfo());
+
+    // Search engine selection.
+    $temp->add(new admin_setting_heading('searchengineheading', new lang_string('searchengine', 'admin'), ''));
+    $temp->add(new admin_setting_configselect('searchengine',
+                                new lang_string('selectsearchengine', 'admin'), '', 'solr', $engines));
+
+    // Enable search areas.
+    $temp->add(new admin_setting_heading('searchareasheading', new lang_string('availablesearchareas', 'admin'), ''));
+    $searchareas = \core_search\manager::get_search_areas_list();
+    foreach ($searchareas as $areaid => $searcharea) {
+        list($componentname, $varname) = $searcharea->get_config_var_name();
+        $temp->add(new admin_setting_configcheckbox($componentname . '/' . $varname . '_enabled', $searcharea->get_visible_name(true),
+            '', 1, 1, 0));
+    }
+    $ADMIN->add('searchplugins', $temp);
+
+    foreach ($pages as $page) {
+        $ADMIN->add('searchplugins', $page);
+    }
+}
+
 /// Add all admin tools
 if ($hassiteconfig) {
     $ADMIN->add('modules', new admin_category('tools', new lang_string('tools', 'admin')));
index 9cae26c..cc65569 100644 (file)
@@ -51,4 +51,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $optionalsubsystems->add(new admin_setting_configcheckbox('enableplagiarism', new lang_string('enableplagiarism','plagiarism'), new lang_string('configenableplagiarism','plagiarism'), 0));
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('enablebadges', new lang_string('enablebadges', 'badges'), new lang_string('configenablebadges', 'badges'), 1));
+
+    $optionalsubsystems->add(new admin_setting_configcheckbox('enableglobalsearch', new lang_string('enableglobalsearch', 'admin'),
+        new lang_string('enableglobalsearch_desc', 'admin'), 0, 1, 0));
 }
index 7aa2056..d40eec0 100644 (file)
@@ -182,6 +182,8 @@ class store implements \tool_log\log\store, \core\log\sql_reader {
 
     /**
      * Legacy add_to_log() code.
+     * @deprecated since Moodle 3.1 MDL-45104 - Please use supported log stores such as "standard" or "external" instead.
+     * @todo MDL-52805 This will be removed in Moodle 3.3
      *
      * @param    int $courseid The course id
      * @param    string $module The module name  e.g. forum, journal, resource, course, user etc
index 4b6f344..ed6e53c 100644 (file)
@@ -87,13 +87,9 @@ if ($mform && ($mform->is_cancelled() || !empty($CFG->preventscheduledtaskchange
 
         try {
             \core\task\manager::configure_scheduled_task($task);
-            $url = $PAGE->url;
-            $url->params(array('success'=>get_string('changessaved')));
-            redirect($url);
+            redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
         } catch (Exception $e) {
-            $url = $PAGE->url;
-            $url->params(array('error'=>$e->getMessage()));
-            redirect($url);
+            redirect($PAGE->url, $e->getMessage(), null, \core\output\notification::NOTIFY_ERROR);
         }
     } else {
         echo $OUTPUT->header();
@@ -104,19 +100,7 @@ if ($mform && ($mform->is_cancelled() || !empty($CFG->preventscheduledtaskchange
 
 } else {
     echo $OUTPUT->header();
-    $error = optional_param('error', '', PARAM_NOTAGS);
-    if ($error) {
-        echo $OUTPUT->notification($error, 'notifyerror');
-    }
-    $success = optional_param('success', '', PARAM_NOTAGS);
-    if ($success) {
-        echo $OUTPUT->notification($success, 'notifysuccess');
-    }
     $tasks = core\task\manager::get_all_scheduled_tasks();
     echo $renderer->scheduled_tasks_table($tasks);
     echo $OUTPUT->footer();
 }
-
-
-
-
index 9b09541..ac31ab6 100644 (file)
@@ -72,10 +72,10 @@ class tool_templatelibrary_external_testcase extends externallib_advanced_testca
         // Change the theme to 'base' because it overrides these templates.
         $CFG->theme = 'base';
 
-        $template = external::load_canonical_template('core', 'notification_problem');
+        $template = external::load_canonical_template('core', 'notification_error');
 
         // Only the base template should contain the docs.
-        $this->assertContains('@template core/notification_problem', $template);
+        $this->assertContains('@template core/notification_error', $template);
 
         // Restore the original theme.
         $CFG->theme = $originaltheme;
index 958d6c1..7e2d4c5 100644 (file)
@@ -60,12 +60,7 @@ foreach ($allcohorts as $c) {
 unset($allcohorts);
 
 if (count($cohorts) < 2) {
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('bulkadd', 'core_cohort'));
-    echo $OUTPUT->notification(get_string('bulknocohort', 'core_cohort'));
-    echo $OUTPUT->continue_button(new moodle_url('/admin/user/user_bulk.php'));
-    echo $OUTPUT->footer();
-    die;
+    redirect(new moodle_url('/admin/user/user_bulk.php'), get_string('bulknocohort', 'core_cohort'));
 }
 
 $countries = get_string_manager()->get_list_of_countries(true);
index 31843a2..db43172 100644 (file)
@@ -115,8 +115,7 @@ if ($activate) {
     $url = new moodle_url('/badges/action.php', $params);
 
     if (!$badge->has_criteria()) {
-        echo $OUTPUT->notification(get_string('error:cannotact', 'badges') . get_string('nocriteria', 'badges'));
-        echo $OUTPUT->continue_button($returnurl);
+        redirect($returnurl, get_string('error:cannotact', 'badges') . get_string('nocriteria', 'badges'), null, \core\output\notification::NOTIFY_ERROR);
     } else {
         $message = get_string('reviewconfirm', 'badges', $badge->name);
         echo $OUTPUT->confirm($message, $url, $returnurl);
diff --git a/blocks/globalsearch/block_globalsearch.php b/blocks/globalsearch/block_globalsearch.php
new file mode 100644 (file)
index 0000000..5761d4c
--- /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/>.
+
+/**
+ * Global search block.
+ *
+ * @package    block_globalsearch
+ * @copyright  Prateek Sachan {@link http://prateeksachan.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Global search block.
+ *
+ * @package    block_globalsearch
+ * @copyright  Prateek Sachan {@link http://prateeksachan.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_globalsearch extends block_base {
+
+    /**
+     * Initialises the block.
+     *
+     * @return void
+     */
+    public function init() {
+        $this->title = get_string('pluginname', 'block_globalsearch');
+    }
+
+    /**
+     * Gets the block contents.
+     *
+     * If we can avoid it better not check the server status here as connecting
+     * to the server will slow down the whole page load.
+     *
+     * @return string The block HTML.
+     */
+    public function get_content() {
+        global $OUTPUT;
+        if ($this->content !== null) {
+            return $this->content;
+        }
+
+        $this->content = new stdClass();
+        $this->content->footer = '';
+
+        if (\core_search\manager::is_global_search_enabled() === false) {
+            $this->content->text = get_string('globalsearchdisabled', 'search');
+            return $this->content;
+        }
+
+        $url = new moodle_url('/search/index.php');
+        $this->content->footer .= html_writer::link($url, get_string('advancedsearch', 'search'));
+
+        $this->content->text  = html_writer::start_tag('div', array('class' => 'searchform'));
+        $this->content->text .= html_writer::start_tag('form', array('action' => $url->out()));
+        $this->content->text .= html_writer::start_tag('fieldset', array('action' => 'invisiblefieldset'));
+
+        // Input.
+        $this->content->text .= html_writer::tag('label', get_string('search', 'search'),
+            array('for' => 'searchform_search', 'class' => 'accesshide'));
+        $inputoptions = array('id' => 'searchform_search', 'name' => 'q', 'type' => 'text', 'size' => '15');
+        $this->content->text .= html_writer::empty_tag('input', $inputoptions);
+
+        // Search button.
+        $this->content->text .= html_writer::tag('button', get_string('search', 'search'),
+            array('id' => 'searchform_button', 'type' => 'submit', 'title' => 'globalsearch'));
+        $this->content->text .= html_writer::end_tag('fieldset');
+        $this->content->text .= html_writer::end_tag('form');
+        $this->content->text .= html_writer::end_tag('div');
+
+        return $this->content;
+    }
+}
diff --git a/blocks/globalsearch/db/access.php b/blocks/globalsearch/db/access.php
new file mode 100644 (file)
index 0000000..ad9f49d
--- /dev/null
@@ -0,0 +1,48 @@
+<?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/>.
+
+/**
+ * Global search Block caps.
+ *
+ * @package    block_globalsearch
+ * @copyright  Prateek Sachan {@link http://prateeksachan.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = array(
+
+    'block/globalsearch:myaddinstance' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'archetypes' => array(
+            'user' => CAP_ALLOW
+        ),
+
+        'clonepermissionsfrom' => 'moodle/my:manageblocks'
+    ),
+
+    'block/globalsearch:addinstance' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_BLOCK,
+        'archetypes' => array(
+            'manager' => CAP_ALLOW
+        ),
+
+        'clonepermissionsfrom' => 'moodle/site:manageblocks'
+    ),
+);
diff --git a/blocks/globalsearch/lang/en/block_globalsearch.php b/blocks/globalsearch/lang/en/block_globalsearch.php
new file mode 100644 (file)
index 0000000..8a0635b
--- /dev/null
@@ -0,0 +1,27 @@
+<?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/>.
+
+/**
+ * Strings for component 'block_globalsearch'.
+ *
+ * @package    block_globalsearch
+ * @copyright  Prateek Sachan {@link http://prateeksachan.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['globalsearch:addinstance'] = 'Add a new global search block';
+$string['globalsearch:myaddinstance'] = 'Add a new global search block to Dashboard';
+$string['pluginname'] = 'Global search';
diff --git a/blocks/globalsearch/version.php b/blocks/globalsearch/version.php
new file mode 100644 (file)
index 0000000..07a5fa4
--- /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/>.
+
+/**
+ * Global Search version details.
+ *
+ * @package    block_globalsearch
+ * @copyright  Prateek Sachan {@link http://prateeksachan.com}
+ * @copyright  Daniel Neis
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$plugin->version = 2016012000;
+$plugin->requires  = 2015111000;
+$plugin->component = 'block_globalsearch';
index 131c559..86565e8 100644 (file)
@@ -70,7 +70,8 @@ class block_navigation_renderer extends plugin_renderer_base {
 
         // Turn our navigation items into list items.
         $lis = array();
-        $number = 0;
+        // Set the number to be static for unique id's.
+        static $number = 0;
         foreach ($items as $item) {
             $number++;
             if (!$item->display && !$item->contains_active_node()) {
index 71f44f5..7676171 100644 (file)
@@ -31,6 +31,7 @@ A definition:
             'invalidationevents' => array(            // Optional
                 'contextmarkeddirty'
             ),
+            'canuselocalstore' => false               // Optional
             'sharingoptions' => null                  // Optional
             'defaultsharing' => null                  // Optional
         )
@@ -151,7 +152,7 @@ The following optional settings can also be defined:
 * invalidationevents - An array of events that should trigger this cache to invalidate.
 * sharingoptions - The sum of the possible sharing options that are applicable to the definition. An advanced setting.
 * defaultsharing - The default sharing option to use. It's highly recommended that you don't set this unless there is a very specific reason not to use the system default.
-
+* canuselocalstore - The default is to required a shared cache location for all nodes in a multi webserver environment.  If the cache uses revisions and never updates key data, administrators can use a local storage cache for this cache.
 It's important to note that internally the definition is also aware of the component. This is picked up when the definition is read, based upon the location of the caches.php file.
 
 The staticacceleration option.
@@ -269,4 +270,4 @@ There are a couple of considerations to using this method:
 
 Please be aware that if you are using Memcache or Memcached it is recommended to use dedicated Memcached servers.
 When caches get purged the memcached servers you have configured get purged, any data stored within them whether it belongs to Moodle or not will be removed.
-If you are using Memcached for sessions as well as caching/testing and caches get purged your sessions will be removed prematurely and users will be need to start again.
\ No newline at end of file
+If you are using Memcached for sessions as well as caching/testing and caches get purged your sessions will be removed prematurely and users will be need to start again.
index a5fd64a..21c85f2 100644 (file)
@@ -100,6 +100,11 @@ defined('MOODLE_INTERNAL') || die();
  *     + defaultsharing
  *          [int] The default sharing option to use. It's highly recommended that you don't set this unless there is a very
  *          specific reason not to use the system default.
+ *     + canuselocalstore
+ *          [bool] The cache is able to safely run with multiple copies on different webservers without any need for administrator
+ *                 intervention to ensure that data stays in sync across nodes.  This is usually managed by a revision
+ *                 system as seen in modinfo cache or language cache.  Requiring purge on upgrade is not sufficient as
+ *                 it requires administrator intervention on each node to make it work.
  *
  * For examples take a look at lib/db/caches.php
  *
@@ -308,6 +313,12 @@ class cache_definition {
      */
     protected $sharingoptions;
 
+    /**
+     * Whether this cache supports local storages.
+     * @var bool
+     */
+    protected $canuselocalstore = false;
+
     /**
      * The selected sharing option.
      * @var int One of self::SHARING_*
@@ -367,6 +378,7 @@ class cache_definition {
         $sharingoptions = self::SHARING_DEFAULT;
         $selectedsharingoption = self::SHARING_DEFAULT;
         $userinputsharingkey = '';
+        $canuselocalstore = false;
 
         if (array_key_exists('simplekeys', $definition)) {
             $simplekeys = (bool)$definition['simplekeys'];
@@ -453,6 +465,9 @@ class cache_definition {
                 $selectedsharingoption = self::SHARING_ALL;
             }
         }
+        if (array_key_exists('canuselocalstore', $definition)) {
+            $canuselocalstore = (bool)$definition['canuselocalstore'];
+        }
 
         if (array_key_exists('userinputsharingkey', $definition) && !empty($definition['userinputsharingkey'])) {
             $userinputsharingkey = (string)$definition['userinputsharingkey'];
@@ -529,6 +544,7 @@ class cache_definition {
         $cachedefinition->sharingoptions = $sharingoptions;
         $cachedefinition->selectedsharingoption = $selectedsharingoption;
         $cachedefinition->userinputsharingkey = $userinputsharingkey;
+        $cachedefinition->canuselocalstore = $canuselocalstore;
 
         return $cachedefinition;
     }
@@ -732,6 +748,15 @@ class cache_definition {
         return $this->requirelockingwrite;
     }
 
+    /**
+     * Returns true if this definition allows local storage to be used for caching.
+     * @since Moodle 3.1.0
+     * @return bool
+     */
+    public function can_use_localstore() {
+        return $this->canuselocalstore;
+    }
+
     /**
      * Returns true if this definition requires a searchable cache.
      * @since Moodle 2.4.4
@@ -766,13 +791,14 @@ class cache_definition {
      * Sets the identifiers for this definition, or updates them if they have already been set.
      *
      * @param array $identifiers
+     * @return bool false if no identifiers where changed, true otherwise.
      * @throws coding_exception
      */
     public function set_identifiers(array $identifiers = array()) {
         // If we are setting the exact same identifiers then just return as nothing really changed.
         // We don't care about order as cache::make will use the same definition order all the time.
         if ($identifiers === $this->identifiers) {
-            return;
+            return false;
         }
 
         foreach ($this->requireidentifiers as $identifier) {
@@ -791,6 +817,8 @@ class cache_definition {
         // Reset the key prefix's they need updating now.
         $this->keyprefixsingle = null;
         $this->keyprefixmulti = null;
+
+        return true;
     }
 
     /**
index 14eecd8..4050838 100644 (file)
@@ -190,9 +190,9 @@ class cachestore_dummy extends cache_store {
             foreach ($keyvaluearray as $pair) {
                 $this->store[$pair['key']] = $pair['value'];
             }
-            return count($keyvaluearray);
+
         }
-        return 0;
+        return count($keyvaluearray);
     }
 
     /**
index e0a7723..544718c 100644 (file)
@@ -264,7 +264,15 @@ class cache implements cache_loader {
      * @param array $identifiers
      */
     public function set_identifiers(array $identifiers) {
-        $this->definition->set_identifiers($identifiers);
+        if ($this->definition->set_identifiers($identifiers)) {
+            // As static acceleration uses input keys and not parsed keys
+            // it much be cleared when the identifier set is changed.
+            $this->staticaccelerationarray = array();
+            if ($this->staticaccelerationsize !== false) {
+                $this->staticaccelerationkeys = array();
+                $this->staticaccelerationcount = 0;
+            }
+        }
     }
 
     /**
@@ -278,23 +286,19 @@ class cache implements cache_loader {
      * @throws coding_exception
      */
     public function get($key, $strictness = IGNORE_MISSING) {
-        // 1. Parse the key.
-        $parsedkey = $this->parse_key($key);
-        // 2. Get it from the static acceleration array if we can (only when it is enabled and it has already been requested/set).
-        $result = false;
-        if ($this->use_static_acceleration()) {
-            $result = $this->static_acceleration_get($parsedkey);
-        }
-        if ($result !== false) {
-            if (!is_scalar($result)) {
-                // If data is an object it will be a reference.
-                // If data is an array if may contain references.
-                // We want to break references so that the cache cannot be modified outside of itself.
-                // Call the function to unreference it (in the best way possible).
-                $result = $this->unref($result);
+        // 1. Get it from the static acceleration array if we can (only when it is enabled and it has already been requested/set).
+        $usesstaticacceleration = $this->use_static_acceleration();
+
+        if ($usesstaticacceleration) {
+            $result = $this->static_acceleration_get($key);
+            if ($result !== false) {
+                return $result;
             }
-            return $result;
         }
+
+        // 2. Parse the key.
+        $parsedkey = $this->parse_key($key);
+
         // 3. Get it from the store. Obviously wasn't in the static acceleration array.
         $result = $this->store->get($parsedkey);
         if ($result !== false) {
@@ -309,10 +313,11 @@ class cache implements cache_loader {
             if ($result instanceof cache_cached_object) {
                 $result = $result->restore_object();
             }
-            if ($this->use_static_acceleration()) {
-                $this->static_acceleration_set($parsedkey, $result);
+            if ($usesstaticacceleration) {
+                $this->static_acceleration_set($key, $result);
             }
         }
+
         // 4. Load if from the loader/datasource if we don't already have it.
         $setaftervalidation = false;
         if ($result === false) {
@@ -341,7 +346,7 @@ class cache implements cache_loader {
         }
         // 7. Make sure we don't pass back anything that could be a reference.
         //    We don't want people modifying the data in the cache.
-        if (!is_scalar($result)) {
+        if (!$this->store->supports_dereferencing_objects() && !is_scalar($result)) {
             // If data is an object it will be a reference.
             // If data is an array if may contain references.
             // We want to break references so that the cache cannot be modified outside of itself.
@@ -385,7 +390,7 @@ class cache implements cache_loader {
             $parsedkeys[$pkey] = $key;
             $keystofind[$pkey] = $key;
             if ($isusingpersist) {
-                $value = $this->static_acceleration_get($pkey);
+                $value = $this->static_acceleration_get($key);
                 if ($value !== false) {
                     $resultpersist[$pkey] = $value;
                     unset($keystofind[$pkey]);
@@ -409,7 +414,7 @@ class cache implements cache_loader {
                     $value = $value->restore_object();
                 }
                 if ($value !== false && $this->use_static_acceleration()) {
-                    $this->static_acceleration_set($key, $value);
+                    $this->static_acceleration_set($keystofind[$key], $value);
                 }
                 $resultstore[$key] = $value;
             }
@@ -450,6 +455,13 @@ class cache implements cache_loader {
         // Create an array with the original keys and the found values. This will be what we return.
         $fullresult = array();
         foreach ($result as $key => $value) {
+            if (!is_scalar($value)) {
+                // If data is an object it will be a reference.
+                // If data is an array if may contain references.
+                // We want to break references so that the cache cannot be modified outside of itself.
+                // Call the function to unreference it (in the best way possible).
+                $value = $this->unref($value);
+            }
             $fullresult[$parsedkeys[$key]] = $value;
         }
         unset($result);
@@ -507,22 +519,27 @@ class cache implements cache_loader {
             // We have to let the loader do its own parsing of data as it may be unique.
             $this->loader->set($key, $data);
         }
+        $usestaticacceleration = $this->use_static_acceleration();
+
         if (is_object($data) && $data instanceof cacheable_object) {
             $data = new cache_cached_object($data);
-        } else if (!is_scalar($data)) {
+        } else if (!$this->store->supports_dereferencing_objects() && !is_scalar($data)) {
             // If data is an object it will be a reference.
             // If data is an array if may contain references.
             // We want to break references so that the cache cannot be modified outside of itself.
             // Call the function to unreference it (in the best way possible).
             $data = $this->unref($data);
         }
+
+        if ($usestaticacceleration) {
+            $this->static_acceleration_set($key, $data);
+        }
+
         if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
             $data = new cache_ttl_wrapper($data, $this->definition->get_ttl());
         }
         $parsedkey = $this->parse_key($key);
-        if ($this->use_static_acceleration()) {
-            $this->static_acceleration_set($parsedkey, $data);
-        }
+
         return $this->store->set($parsedkey, $data);
     }
 
@@ -626,16 +643,20 @@ class cache implements cache_loader {
         $data = array();
         $simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
         $usestaticaccelerationarray = $this->use_static_acceleration();
+        $needsdereferencing = !$this->store->supports_dereferencing_objects();
         foreach ($keyvaluearray as $key => $value) {
             if (is_object($value) && $value instanceof cacheable_object) {
                 $value = new cache_cached_object($value);
-            } else if (!is_scalar($value)) {
+            } else if ($needsdereferencing && !is_scalar($value)) {
                 // If data is an object it will be a reference.
                 // If data is an array if may contain references.
                 // We want to break references so that the cache cannot be modified outside of itself.
                 // Call the function to unreference it (in the best way possible).
                 $value = $this->unref($value);
             }
+            if ($usestaticaccelerationarray) {
+                $this->static_acceleration_set($key, $value);
+            }
             if ($simulatettl) {
                 $value = new cache_ttl_wrapper($value, $this->definition->get_ttl());
             }
@@ -643,9 +664,6 @@ class cache implements cache_loader {
                 'key' => $this->parse_key($key),
                 'value' => $value
             );
-            if ($usestaticaccelerationarray) {
-                $this->static_acceleration_set($data[$key]['key'], $value);
-            }
         }
         $successfullyset = $this->store->set_many($data);
         if ($this->perfdebug && $successfullyset) {
@@ -676,11 +694,12 @@ class cache implements cache_loader {
      * @return bool True if the cache has the requested key, false otherwise.
      */
     public function has($key, $tryloadifpossible = false) {
-        $parsedkey = $this->parse_key($key);
-        if ($this->static_acceleration_has($parsedkey)) {
+        if ($this->static_acceleration_has($key)) {
             // Hoorah, that was easy. It exists in the static acceleration array so we definitely have it.
             return true;
         }
+        $parsedkey = $this->parse_key($key);
+
         if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
             // The data has a TTL and the store doesn't support it natively.
             // We must fetch the data and expect a ttl wrapper.
@@ -760,17 +779,13 @@ class cache implements cache_loader {
         }
 
         if ($this->use_static_acceleration()) {
-            $parsedkeys = array();
             foreach ($keys as $id => $key) {
-                $parsedkey = $this->parse_key($key);
-                if ($this->static_acceleration_has($parsedkey)) {
+                if ($this->static_acceleration_has($key)) {
                     return true;
                 }
-                $parsedkeys[] = $parsedkey;
             }
-        } else {
-            $parsedkeys = array_map(array($this, 'parse_key'), $keys);
         }
+        $parsedkeys = array_map(array($this, 'parse_key'), $keys);
         return $this->store->has_any($parsedkeys);
     }
 
@@ -783,12 +798,12 @@ class cache implements cache_loader {
      * @return bool True of success, false otherwise.
      */
     public function delete($key, $recurse = true) {
-        $parsedkey = $this->parse_key($key);
-        $this->static_acceleration_delete($parsedkey);
+        $this->static_acceleration_delete($key);
         if ($recurse && $this->loader !== false) {
             // Delete from the bottom of the stack first.
             $this->loader->delete($key, $recurse);
         }
+        $parsedkey = $this->parse_key($key);
         return $this->store->delete($parsedkey);
     }
 
@@ -801,16 +816,16 @@ class cache implements cache_loader {
      * @return int The number of items successfully deleted.
      */
     public function delete_many(array $keys, $recurse = true) {
-        $parsedkeys = array_map(array($this, 'parse_key'), $keys);
         if ($this->use_static_acceleration()) {
-            foreach ($parsedkeys as $parsedkey) {
-                $this->static_acceleration_delete($parsedkey);
+            foreach ($keys as $key) {
+                $this->static_acceleration_delete($key);
             }
         }
         if ($recurse && $this->loader !== false) {
             // Delete from the bottom of the stack first.
             $this->loader->delete_many($keys, $recurse);
         }
+        $parsedkeys = array_map(array($this, 'parse_key'), $keys);
         return $this->store->delete_many($parsedkeys);
     }
 
@@ -974,19 +989,11 @@ class cache implements cache_loader {
      * @return bool
      */
     protected function static_acceleration_has($key) {
-        // This method of checking if an array was supplied is faster than is_array.
-        if ($key === (array)$key) {
-            $key = $key['key'];
-        }
         // This could be written as a single line, however it has been split because the ttl check is faster than the instanceof
         // and has_expired calls.
-        if (!$this->staticacceleration || !array_key_exists($key, $this->staticaccelerationarray)) {
+        if (!$this->staticacceleration || !isset($this->staticaccelerationarray[$key])) {
             return false;
         }
-        if ($this->has_a_ttl() && $this->store_supports_native_ttl()) {
-             return !($this->staticaccelerationarray[$key] instanceof cache_ttl_wrapper &&
-                      $this->staticaccelerationarray[$key]->has_expired());
-        }
         return true;
     }
 
@@ -1007,34 +1014,20 @@ class cache implements cache_loader {
      * Returns the item from the static acceleration array if it exists there.
      *
      * @param string $key The parsed key
-     * @return mixed|false The data from the static acceleration array or false if it wasn't there.
+     * @return mixed|false Dereferenced data from the static acceleration array or false if it wasn't there.
      */
     protected function static_acceleration_get($key) {
-        // This method of checking if an array was supplied is faster than is_array.
-        if ($key === (array)$key) {
-            $key = $key['key'];
-        }
-        // This isset check is faster than array_key_exists but will return false
-        // for null values, meaning null values will come from backing store not
-        // the static acceleration array. We think this okay because null usage should be
-        // very rare (see comment in MDL-39472).
         if (!$this->staticacceleration || !isset($this->staticaccelerationarray[$key])) {
             $result = false;
         } else {
-            $data = $this->staticaccelerationarray[$key];
-            if (!$this->has_a_ttl() || !$data instanceof cache_ttl_wrapper) {
-                if ($data instanceof cache_cached_object) {
-                    $data = $data->restore_object();
-                }
-                $result = $data;
-            } else if ($data->has_expired()) {
-                $this->static_acceleration_delete($key);
-                $result = false;
+            $data = $this->staticaccelerationarray[$key]['data'];
+
+            if ($data instanceof cache_cached_object) {
+                $result = $data->restore_object();
+            } else if ($this->staticaccelerationarray[$key]['serialized']) {
+                $result = unserialize($data);
             } else {
-                if ($data instanceof cache_cached_object) {
-                    $data = $data->restore_object();
-                }
-                $result = $data->data;
+                $result = $data;
             }
         }
         if ($result) {
@@ -1081,15 +1074,23 @@ class cache implements cache_loader {
      * @return bool
      */
     protected function static_acceleration_set($key, $data) {
-        // This method of checking if an array was supplied is faster than is_array.
-        if ($key === (array)$key) {
-            $key = $key['key'];
-        }
         if ($this->staticaccelerationsize !== false && isset($this->staticaccelerationkeys[$key])) {
             $this->staticaccelerationcount--;
             unset($this->staticaccelerationkeys[$key]);
         }
-        $this->staticaccelerationarray[$key] = $data;
+
+        // We serialize anything that's not;
+        // 1. A known scalar safe value.
+        // 2. A definition that says it's simpledata.  We trust it that it doesn't contain dangerous references.
+        // 3. An object that handles dereferencing by itself.
+        if (is_scalar($data) || $this->definition->uses_simple_data()
+                || $data instanceof cache_cached_object) {
+            $this->staticaccelerationarray[$key]['data'] = $data;
+            $this->staticaccelerationarray[$key]['serialized'] = false;
+        } else {
+            $this->staticaccelerationarray[$key]['data'] = serialize($data);
+            $this->staticaccelerationarray[$key]['serialized'] = true;
+        }
         if ($this->staticaccelerationsize !== false) {
             $this->staticaccelerationcount++;
             $this->staticaccelerationkeys[$key] = $key;
@@ -1123,12 +1124,9 @@ class cache implements cache_loader {
      */
     protected function static_acceleration_delete($key) {
         unset($this->staticaccelerationarray[$key]);
-        if ($this->staticaccelerationsize !== false) {
-            $dropkey = array_search($key, $this->staticaccelerationkeys);
-            if ($dropkey) {
-                unset($this->staticaccelerationkeys[$dropkey]);
-                $this->staticaccelerationcount--;
-            }
+        if ($this->staticaccelerationsize !== false && isset($this->staticaccelerationkeys[$key])) {
+            unset($this->staticaccelerationkeys[$key]);
+            $this->staticaccelerationcount--;
         }
         return true;
     }
@@ -1806,7 +1804,7 @@ class cache_session extends cache {
         }
         // 6. Make sure we don't pass back anything that could be a reference.
         //    We don't want people modifying the data in the cache.
-        if (!is_scalar($result)) {
+        if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($result)) {
             // If data is an object it will be a reference.
             // If data is an array if may contain references.
             // We want to break references so that the cache cannot be modified outside of itself.
@@ -1846,7 +1844,7 @@ class cache_session extends cache {
         }
         if (is_object($data) && $data instanceof cacheable_object) {
             $data = new cache_cached_object($data);
-        } else if (!is_scalar($data)) {
+        } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($data)) {
             // If data is an object it will be a reference.
             // If data is an array if may contain references.
             // We want to break references so that the cache cannot be modified outside of itself.
@@ -1922,6 +1920,12 @@ class cache_session extends cache {
             if ($value instanceof cache_cached_object) {
                 /* @var cache_cached_object $value */
                 $value = $value->restore_object();
+            } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($value)) {
+                // If data is an object it will be a reference.
+                // If data is an array if may contain references.
+                // We want to break references so that the cache cannot be modified outside of itself.
+                // Call the function to unreference it (in the best way possible).
+                $value = $this->unref($value);
             }
             $return[$key] = $value;
             if ($value === false) {
@@ -2027,7 +2031,7 @@ class cache_session extends cache {
         foreach ($keyvaluearray as $key => $value) {
             if (is_object($value) && $value instanceof cacheable_object) {
                 $value = new cache_cached_object($value);
-            } else if (!is_scalar($value)) {
+            } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($value)) {
                 // If data is an object it will be a reference.
                 // If data is an array if may contain references.
                 // We want to break references so that the cache cannot be modified outside of itself.
index 2c4b099..c415ba9 100644 (file)
@@ -126,6 +126,14 @@ abstract class cache_store implements cache_store_interface {
      */
     const IS_SEARCHABLE = 8;
 
+    /**
+     * The cache store dereferences objects.
+     *
+     * When set, loaders will assume that all data coming from this store has already had all references
+     * resolved.  So even for complex object structures it will not try to remove references again.
+     */
+    const DEREFERENCES_OBJECTS = 16;
+
     // Constants for the modes of a cache store
 
     /**
@@ -334,6 +342,15 @@ abstract class cache_store implements cache_store_interface {
         return in_array('cache_is_searchable', class_implements($this));
     }
 
+    /**
+     * Returns true if the store automatically dereferences objects.
+     *
+     * @return bool
+     */
+    public function supports_dereferencing_objects() {
+        return $this::get_supported_features() & self::DEREFERENCES_OBJECTS;
+    }
+
     /**
      * Creates a clone of this store instance ready to be initialised.
      *
index 5a2ef3b..e482702 100644 (file)
@@ -132,6 +132,8 @@ class cache_definition_mappings_form extends moodleform {
      * The definition of the form
      */
     protected final function definition() {
+        global $OUTPUT;
+
         $definition = $this->_customdata['definition'];
         $form = $this->_form;
 
@@ -139,6 +141,14 @@ class cache_definition_mappings_form extends moodleform {
         list($currentstores, $storeoptions, $defaults) =
                 cache_administration_helper::get_definition_store_options($component, $area);
 
+        $storedata = cache_administration_helper::get_definition_summaries();
+        if ($storedata[$definition]['mode'] != cache_store::MODE_REQUEST) {
+            if (isset($storedata[$definition]['canuselocalstore']) && $storedata[$definition]['canuselocalstore']) {
+                $form->addElement('html', $OUTPUT->notification(get_string('localstorenotification', 'cache'), 'notifymessage'));
+            } else {
+                $form->addElement('html', $OUTPUT->notification(get_string('sharedstorenotification', 'cache'), 'notifymessage'));
+            }
+        }
         $form->addElement('hidden', 'definition', $definition);
         $form->setType('definition', PARAM_SAFEPATH);
         $form->addElement('hidden', 'action', 'editdefinitionmapping');
index 58dcdd8..2e6813e 100644 (file)
@@ -806,6 +806,7 @@ abstract class cache_administration_helper extends cache_helper {
                 'component' => $definition->get_component(),
                 'area' => $definition->get_area(),
                 'mappings' => $mappings,
+                'canuselocalstore' => $definition->can_use_localstore(),
                 'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
                 'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
                 'userinputsharingkey' => $definition->get_user_input_sharing_key()
index e044c91..67a6642 100644 (file)
@@ -211,7 +211,8 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
     public static function get_supported_features(array $configuration = array()) {
         $supported = self::SUPPORTS_DATA_GUARANTEE +
                      self::SUPPORTS_NATIVE_TTL +
-                     self::IS_SEARCHABLE;
+                     self::IS_SEARCHABLE +
+                     self::DEREFERENCES_OBJECTS;
         return $supported;
     }
 
index 77efd21..e4ead7f 100644 (file)
@@ -271,7 +271,7 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
      * @return int
      */
     public static function get_supported_features(array $configuration = array()) {
-        return self::SUPPORTS_NATIVE_TTL;
+        return self::SUPPORTS_NATIVE_TTL + self::DEREFERENCES_OBJECTS;
     }
 
     /**
index 0ed6623..5e8250d 100644 (file)
@@ -258,7 +258,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
      * @return int
      */
     public static function get_supported_features(array $configuration = array()) {
-        return self::SUPPORTS_NATIVE_TTL;
+        return self::SUPPORTS_NATIVE_TTL + self::DEREFERENCES_OBJECTS;
     }
 
     /**
index 7ea787d..641b051 100644 (file)
@@ -175,7 +175,7 @@ class cachestore_mongodb extends cache_store implements cache_is_configurable {
      * @return int
      */
     public static function get_supported_features(array $configuration = array()) {
-        $supports = self::SUPPORTS_DATA_GUARANTEE;
+        $supports = self::SUPPORTS_DATA_GUARANTEE + self::DEREFERENCES_OBJECTS;
         if (array_key_exists('extendedmode', $configuration) && $configuration['extendedmode']) {
             $supports += self::SUPPORTS_MULTIPLE_IDENTIFIERS;
         }
index 6c9f14c..26ab145 100644 (file)
@@ -407,7 +407,7 @@ class core_cache_testcase extends advanced_testcase {
         $this->assertEquals('pork', $var->subobj->subobj->key);
         $this->assertTrue($cache->delete('obj'));
 
-        // Death reference test... basicaly we don't want this to die.
+        // Death reference test... basically we don't want this to die.
         $obj = new stdClass;
         $obj->key = 'value';
         $obj->self =& $obj;
@@ -433,6 +433,32 @@ class core_cache_testcase extends advanced_testcase {
 
         $this->assertTrue($cache->delete('obj'));
 
+        // Death reference test on get_many... basically we don't want this to die.
+        $obj = new stdClass;
+        $obj->key = 'value';
+        $obj->self =& $obj;
+        $this->assertEquals(1, $cache->set_many(array('obj' => $obj)));
+        $var = $cache->get_many(array('obj'));
+        $this->assertInstanceOf('stdClass', $var['obj']);
+        $this->assertEquals('value', $var['obj']->key);
+
+        // Reference test after retrieve.
+        $obj = new stdClass;
+        $obj->key = 'value';
+        $this->assertEquals(1, $cache->set_many(array('obj' => $obj)));
+
+        $var1 = $cache->get_many(array('obj'));
+        $this->assertInstanceOf('stdClass', $var1['obj']);
+        $this->assertEquals('value', $var1['obj']->key);
+        $var1['obj']->key = 'eulav';
+        $this->assertEquals('eulav', $var1['obj']->key);
+
+        $var2 = $cache->get_many(array('obj'));
+        $this->assertInstanceOf('stdClass', $var2['obj']);
+        $this->assertEquals('value', $var2['obj']->key);
+
+        $this->assertTrue($cache->delete('obj'));
+
         // Test strictness exceptions.
         try {
             $cache->get('exception', MUST_EXIST);
index 8b3ef55..bae500d 100644 (file)
@@ -423,7 +423,6 @@ class cache_phpunit_application extends cache_application {
      * @return false|mixed
      */
     public function phpunit_static_acceleration_get($key) {
-        $key = $this->parse_key($key);
         return $this->static_acceleration_get($key);
     }
 }
index 1c1b133..0b4cbb9 100644 (file)
@@ -1,6 +1,12 @@
 This files describes API changes in /cache/stores/* - cache store plugins.
 Information provided here is intended especially for developers.
 
+=== 3.1 ===
+* Cache stores has a new feature DEREFERENCES_OBJECTS.
+  This allows the cache loader to decide if it needs to handle dereferencing or whether the data
+  coming directly to it has already had references resolved.
+  - see supports_dereferencing_objects in store.php.
+
 === 2.9 ===
 * Cache data source aggregation functionality has been removed. This functionality was found to be broken and unused.
   It was decided that rather than fixing it it should be removed.
index 1ddc12d..95bb480 100644 (file)
@@ -61,6 +61,8 @@ if ($delete === md5($course->timemodified)) {
 
     echo $OUTPUT->header();
     echo $OUTPUT->heading($strdeletingcourse);
+    // This might take a while. Raise the execution time limit.
+    core_php_time_limit::raise();
     // We do this here because it spits out feedback as it goes.
     delete_course($course);
     echo $OUTPUT->heading( get_string("deletedcourse", "", $courseshortname) );
index 3c8e488..dfbbff2 100644 (file)
@@ -38,10 +38,6 @@ class editsection_form extends moodleform {
         $mform->addGroup($elementgroup, 'name_group', get_string('sectionname'), ' ', false);
         $mform->addGroupRule('name_group', array('name' => array(array(get_string('maximumchars', '', 255), 'maxlength', 255))));
 
-        // Add rule for name_group to make sure that the section name is not blank if 'Use default section name'
-        // checkbox is unchecked.
-        $mform->addRule('name_group', get_string('required'), 'required', null, 'client');
-
         $mform->setDefault('usedefaultname', true);
         $mform->setType('name', PARAM_TEXT);
         $mform->disabledIf('name','usedefaultname','checked');
@@ -102,7 +98,7 @@ class editsection_form extends moodleform {
         $editoroptions = $this->_customdata['editoroptions'];
         $default_values = file_prepare_standard_editor($default_values, 'summary', $editoroptions,
                 $editoroptions['context'], 'course', 'section', $default_values->id);
-        $default_values->usedefaultname = (is_null($default_values->name));
+        $default_values->usedefaultname = (strval($default_values->name) === '');
         parent::set_data($default_values);
     }
 
@@ -116,8 +112,9 @@ class editsection_form extends moodleform {
         $data = parent::get_data();
         if ($data !== null) {
             $editoroptions = $this->_customdata['editoroptions'];
+            // Set name as an empty string if use default section name is checked.
             if (!empty($data->usedefaultname)) {
-                $data->name = null;
+                $data->name = '';
             }
             $data = file_postupdate_standard_editor($data, 'summary', $editoroptions,
                     $editoroptions['context'], 'course', 'section', $data->id);
@@ -141,15 +138,6 @@ class editsection_form extends moodleform {
             \core_availability\frontend::report_validation_errors($data, $errors);
         }
 
-        // Validate section name if 'Use default section name' is unchecked.
-        if (empty($data['usedefaultname'])) {
-            // Make sure the trimmed value of section name is not empty.
-            $trimmedname = trim($data['name']);
-            if (empty($trimmedname)) {
-                $errors['name_group'] = get_string('required');
-            }
-        }
-
         return $errors;
     }
 }
index e0ba5c1..344a208 100644 (file)
@@ -139,6 +139,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 wait to be redirected'),
             new Given('I set the following fields to these values:', $data),
             new Given('I press "' . get_string('savechangesandreturntocourse') . '"')
         );
diff --git a/enrol/cohort/edit.php b/enrol/cohort/edit.php
deleted file mode 100644 (file)
index 3affaa9..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_cohort to specified course.
- *
- * @package    enrol_cohort
- * @copyright  2010 Petr Skoda {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require('../../config.php');
-require_once("$CFG->dirroot/enrol/cohort/edit_form.php");
-require_once("$CFG->dirroot/enrol/cohort/locallib.php");
-require_once("$CFG->dirroot/group/lib.php");
-
-$courseid = required_param('courseid', PARAM_INT);
-$instanceid = optional_param('id', 0, PARAM_INT);
-$message = optional_param('message', null, PARAM_TEXT);
-
-$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-$context = context_course::instance($course->id, MUST_EXIST);
-
-require_login($course);
-require_capability('moodle/course:enrolconfig', $context);
-require_capability('enrol/cohort:config', $context);
-
-$PAGE->set_url('/enrol/cohort/edit.php', array('courseid'=>$course->id, 'id'=>$instanceid));
-$PAGE->set_pagelayout('admin');
-
-$returnurl = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
-if (!enrol_is_enabled('cohort')) {
-    redirect($returnurl);
-}
-
-$enrol = enrol_get_plugin('cohort');
-
-if ($instanceid) {
-    $instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'cohort', 'id'=>$instanceid), '*', MUST_EXIST);
-
-} else {
-    // No instance yet, we have to add new instance.
-    if (!$enrol->get_newinstance_link($course->id)) {
-        redirect($returnurl);
-    }
-    navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-    $instance = new stdClass();
-    $instance->id         = null;
-    $instance->courseid   = $course->id;
-    $instance->enrol      = 'cohort';
-    $instance->customint1 = ''; // Cohort id.
-    $instance->customint2 = 0;  // Optional group id.
-}
-
-// Try and make the manage instances node on the navigation active.
-$courseadmin = $PAGE->settingsnav->get('courseadmin');
-if ($courseadmin && $courseadmin->get('users') && $courseadmin->get('users')->get('manageinstances')) {
-    $courseadmin->get('users')->get('manageinstances')->make_active();
-}
-
-
-$mform = new enrol_cohort_edit_form(null, array($instance, $enrol, $course));
-
-if ($mform->is_cancelled()) {
-    redirect($returnurl);
-
-} else if ($data = $mform->get_data()) {
-    if ($data->id) {
-        // NOTE: no cohort changes here!!!
-        if ($data->roleid != $instance->roleid) {
-            // The sync script can only add roles, for perf reasons it does not modify them.
-            role_unassign_all(array('contextid'=>$context->id, 'roleid'=>$instance->roleid, 'component'=>'enrol_cohort', 'itemid'=>$instance->id));
-        }
-        $instance->name         = $data->name;
-        $instance->status       = $data->status;
-        $instance->roleid       = $data->roleid;
-        $instance->customint2   = $data->customint2;
-        $instance->timemodified = time();
-        // Create a new group for the cohort if requested.
-        if ($data->customint2 == COHORT_CREATE_GROUP) {
-            require_capability('moodle/course:managegroups', $context);
-            $groupid = enrol_cohort_create_new_group($course->id, $data->customint1);
-            $instance->customint2 = $groupid;
-        }
-        $DB->update_record('enrol', $instance);
-        \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
-    }  else {
-        // Create a new group for the cohort if requested.
-        if ($data->customint2 == COHORT_CREATE_GROUP) {
-            require_capability('moodle/course:managegroups', $context);
-            $groupid = enrol_cohort_create_new_group($course->id, $data->customint1);
-            $enrol->add_instance($course, array('name' => $data->name, 'status' => $data->status,
-                'customint1' => $data->customint1, 'roleid' => $data->roleid, 'customint2' => $groupid));
-        } else {
-            $enrol->add_instance($course, array('name' => $data->name, 'status' => $data->status,
-                'customint1' => $data->customint1, 'roleid' => $data->roleid, 'customint2' => $data->customint2));
-        }
-        if (!empty($data->submitbuttonnext)) {
-            $returnurl = new moodle_url($PAGE->url);
-            $returnurl->param('message', 'added');
-        }
-    }
-    $trace = new null_progress_trace();
-    enrol_cohort_sync($trace, $course->id);
-    $trace->finished();
-    redirect($returnurl);
-}
-
-$PAGE->set_heading($course->fullname);
-$PAGE->set_title(get_string('pluginname', 'enrol_cohort'));
-
-echo $OUTPUT->header();
-if ($message === 'added') {
-    echo $OUTPUT->notification(get_string('instanceadded', 'enrol'), 'notifysuccess');
-}
-$mform->display();
-echo $OUTPUT->footer();
diff --git a/enrol/cohort/edit_form.php b/enrol/cohort/edit_form.php
deleted file mode 100644 (file)
index 2a02a29..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds instance form
- *
- * @package    enrol_cohort
- * @copyright  2010 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/formslib.php");
-
-class enrol_cohort_edit_form extends moodleform {
-
-    function definition() {
-        global $CFG, $DB;
-
-        $mform = $this->_form;
-
-        list($instance, $plugin, $course) = $this->_customdata;
-        $coursecontext = context_course::instance($course->id);
-
-        $enrol = enrol_get_plugin('cohort');
-
-        $groups = array(0 => get_string('none'));
-        if (has_capability('moodle/course:managegroups', $coursecontext)) {
-            $groups[COHORT_CREATE_GROUP] = get_string('creategroup', 'enrol_cohort');
-        }
-
-        foreach (groups_get_all_groups($course->id) as $group) {
-            $groups[$group->id] = format_string($group->name, true, array('context'=>$coursecontext));
-        }
-
-        $mform->addElement('header','general', get_string('pluginname', 'enrol_cohort'));
-
-        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
-        $mform->setType('name', PARAM_TEXT);
-
-        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
-                         ENROL_INSTANCE_DISABLED => get_string('no'));
-        $mform->addElement('select', 'status', get_string('status', 'enrol_cohort'), $options);
-
-        if ($instance->id) {
-            if ($cohort = $DB->get_record('cohort', array('id'=>$instance->customint1))) {
-                $cohorts = array($instance->customint1=>format_string($cohort->name, true, array('context'=>context::instance_by_id($cohort->contextid))));
-            } else {
-                $cohorts = array($instance->customint1=>get_string('error'));
-            }
-            $mform->addElement('select', 'customint1', get_string('cohort', 'cohort'), $cohorts);
-            $mform->setConstant('customint1', $instance->customint1);
-            $mform->hardFreeze('customint1', $instance->customint1);
-
-        } else {
-            $cohorts = array('' => get_string('choosedots'));
-            $allcohorts = cohort_get_available_cohorts($coursecontext, 0, 0, 0);
-            foreach ($allcohorts as $c) {
-                $cohorts[$c->id] = format_string($c->name);
-            }
-            $mform->addElement('select', 'customint1', get_string('cohort', 'cohort'), $cohorts);
-            $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
-        }
-
-        $roles = get_assignable_roles($coursecontext);
-        $roles[0] = get_string('none');
-        $roles = array_reverse($roles, true); // Descending default sortorder.
-        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_cohort'), $roles);
-        $mform->setDefault('roleid', $enrol->get_config('roleid'));
-        if ($instance->id and !isset($roles[$instance->roleid])) {
-            if ($role = $DB->get_record('role', array('id'=>$instance->roleid))) {
-                $roles = role_fix_names($roles, $coursecontext, ROLENAME_ALIAS, true);
-                $roles[$instance->roleid] = role_get_name($role, $coursecontext);
-            } else {
-                $roles[$instance->roleid] = get_string('error');
-            }
-        }
-        $mform->addElement('select', 'customint2', get_string('addgroup', 'enrol_cohort'), $groups);
-
-        $mform->addElement('hidden', 'courseid', null);
-        $mform->setType('courseid', PARAM_INT);
-
-        $mform->addElement('hidden', 'id', null);
-        $mform->setType('id', PARAM_INT);
-
-        if ($instance->id) {
-            $this->add_action_buttons(true);
-        } else {
-            $this->add_add_buttons();
-        }
-
-        $this->set_data($instance);
-    }
-
-    /**
-     * Adds buttons on create new method form
-     */
-    protected function add_add_buttons() {
-        $mform = $this->_form;
-        $buttonarray = array();
-        $buttonarray[0] = $mform->createElement('submit', 'submitbutton', get_string('addinstance', 'enrol'));
-        $buttonarray[1] = $mform->createElement('submit', 'submitbuttonnext', get_string('addinstanceanother', 'enrol'));
-        $buttonarray[2] = $mform->createElement('cancel');
-        $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
-        $mform->closeHeaderBefore('buttonar');
-    }
-
-    function validation($data, $files) {
-        global $DB;
-
-        $errors = parent::validation($data, $files);
-
-        $params = array('roleid'=>$data['roleid'], 'customint1'=>$data['customint1'], 'courseid'=>$data['courseid'], 'id'=>$data['id']);
-        if ($DB->record_exists_select('enrol', "roleid = :roleid AND customint1 = :customint1 AND courseid = :courseid AND enrol = 'cohort' AND id <> :id", $params)) {
-            $errors['roleid'] = get_string('instanceexists', 'enrol_cohort');
-        }
-
-        return $errors;
-    }
-}
index 61be2ad..da38760 100644 (file)
@@ -79,19 +79,6 @@ class enrol_cohort_plugin extends enrol_plugin {
         }
     }
 
-    /**
-     * Returns link to page which may be used to add new instance of enrolment plugin in course.
-     * @param int $courseid
-     * @return moodle_url page url
-     */
-    public function get_newinstance_link($courseid) {
-        if (!$this->can_add_new_instances($courseid)) {
-            return NULL;
-        }
-        // Multiple instances supported - multiple parent courses linked.
-        return new moodle_url('/enrol/cohort/edit.php', array('courseid'=>$courseid));
-    }
-
     /**
      * Given a courseid this function returns true if the user is able to enrol or configure cohorts.
      * AND there are cohorts that the user can view.
@@ -99,7 +86,7 @@ class enrol_cohort_plugin extends enrol_plugin {
      * @param int $courseid
      * @return bool
      */
-    protected function can_add_new_instances($courseid) {
+    public function can_add_instance($courseid) {
         global $CFG;
         require_once($CFG->dirroot . '/cohort/lib.php');
         $coursecontext = context_course::instance($courseid);
@@ -110,27 +97,51 @@ class enrol_cohort_plugin extends enrol_plugin {
     }
 
     /**
-     * Returns edit icons for the page with list of instances.
-     * @param stdClass $instance
-     * @return array
+     * Add new instance of enrol plugin.
+     * @param object $course
+     * @param array $fields instance fields
+     * @return int id of new instance, null if can not be created
      */
-    public function get_action_icons(stdClass $instance) {
-        global $OUTPUT;
-
-        if ($instance->enrol !== 'cohort') {
-            throw new coding_exception('invalid enrol instance!');
+    public function add_instance($course, array $fields = null) {
+
+        if (!empty($fields['customint2']) && $fields['customint2'] == COHORT_CREATE_GROUP) {
+            // Create a new group for the cohort if requested.
+            $context = context_course::instance($course->id);
+            require_capability('moodle/course:managegroups', $context);
+            $groupid = enrol_cohort_create_new_group($course->id, $fields['customint1']);
+            $fields['customint2'] = $groupid;
         }
-        $context = context_course::instance($instance->courseid);
 
-        $icons = array();
+        return parent::add_instance($course, $fields);
+    }
 
-        if (has_capability('enrol/cohort:config', $context)) {
-            $editlink = new moodle_url("/enrol/cohort/edit.php", array('courseid'=>$instance->courseid, 'id'=>$instance->id));
-            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
-                    array('class' => 'iconsmall')));
+    /**
+     * Update instance of enrol plugin.
+     * @param stdClass $instance
+     * @param stdClass $data modified instance fields
+     * @return boolean
+     */
+    public function update_instance($instance, $data) {
+        // NOTE: no cohort changes here!!!
+        $context = context_course::instance($instance->courseid);
+        if ($data->roleid != $instance->roleid) {
+            // The sync script can only add roles, for perf reasons it does not modify them.
+            $params = array(
+                'contextid' => $context->id,
+                'roleid' => $instance->roleid,
+                'component' => 'enrol_cohort',
+                'itemid' => $instance->id
+            );
+            role_unassign_all($params);
+        }
+        // Create a new group for the cohort if requested.
+        if ($data->customint2 == COHORT_CREATE_GROUP) {
+            require_capability('moodle/course:managegroups', $context);
+            $groupid = enrol_cohort_create_new_group($instance->courseid, $data->customint1);
+            $data->customint2 = $groupid;
         }
 
-        return $icons;
+        return parent::update_instance($instance, $data);
     }
 
     /**
@@ -316,6 +327,176 @@ class enrol_cohort_plugin extends enrol_plugin {
         $context = context_course::instance($instance->courseid);
         return has_capability('enrol/cohort:config', $context);
     }
+
+    /**
+     * Return an array of valid options for the status.
+     *
+     * @return array
+     */
+    protected function get_status_options() {
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the cohorts.
+     *
+     * @param stdClass $instance
+     * @param context $context
+     * @return array
+     */
+    protected function get_cohort_options($instance, $context) {
+        global $DB, $CFG;
+
+        require_once($CFG->dirroot . '/cohort/lib.php');
+
+        $cohorts = array();
+
+        if ($instance->id) {
+            if ($cohort = $DB->get_record('cohort', array('id' => $instance->customint1))) {
+                $name = format_string($cohort->name, true, array('context' => context::instance_by_id($cohort->contextid)));
+                $cohorts = array($instance->customint1 => $name);
+            } else {
+                $cohorts = array($instance->customint1 => get_string('error'));
+            }
+        } else {
+            $cohorts = array('' => get_string('choosedots'));
+            $allcohorts = cohort_get_available_cohorts($context, 0, 0, 0);
+            foreach ($allcohorts as $c) {
+                $cohorts[$c->id] = format_string($c->name);
+            }
+        }
+        return $cohorts;
+    }
+
+    /**
+     * Return an array of valid options for the roles.
+     *
+     * @param stdClass $instance
+     * @param context $coursecontext
+     * @return array
+     */
+    protected function get_role_options($instance, $coursecontext) {
+        global $DB;
+
+        $roles = get_assignable_roles($coursecontext);
+        $roles[0] = get_string('none');
+        $roles = array_reverse($roles, true); // Descending default sortorder.
+        if ($instance->id and !isset($roles[$instance->roleid])) {
+            if ($role = $DB->get_record('role', array('id' => $instance->roleid))) {
+                $roles = role_fix_names($roles, $coursecontext, ROLENAME_ALIAS, true);
+                $roles[$instance->roleid] = role_get_name($role, $coursecontext);
+            } else {
+                $roles[$instance->roleid] = get_string('error');
+            }
+        }
+
+        return $roles;
+    }
+
+    /**
+     * Return an array of valid options for the groups.
+     *
+     * @param context $coursecontext
+     * @return array
+     */
+    protected function get_group_options($coursecontext) {
+        $groups = array(0 => get_string('none'));
+        if (has_capability('moodle/course:managegroups', $coursecontext)) {
+            $groups[COHORT_CREATE_GROUP] = get_string('creategroup', 'enrol_cohort');
+        }
+
+        foreach (groups_get_all_groups($coursecontext->instanceid) as $group) {
+            $groups[$group->id] = format_string($group->name, true, array('context' => $coursecontext));
+        }
+
+        return $groups;
+    }
+
+    /**
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
+     */
+    public function use_standard_editing_ui() {
+        return true;
+    }
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $coursecontext
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $coursecontext) {
+        global $DB;
+
+        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
+        $mform->setType('name', PARAM_TEXT);
+
+        $options = $this->get_status_options();
+        $mform->addElement('select', 'status', get_string('status', 'enrol_cohort'), $options);
+
+        $options = $this->get_cohort_options($instance, $coursecontext);
+        $mform->addElement('select', 'customint1', get_string('cohort', 'cohort'), $options);
+        if ($instance->id) {
+            $mform->setConstant('customint1', $instance->customint1);
+            $mform->hardFreeze('customint1', $instance->customint1);
+        } else {
+            $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
+        }
+
+        $roles = $this->get_role_options($instance, $coursecontext);
+        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_cohort'), $roles);
+        $mform->setDefault('roleid', $this->get_config('roleid'));
+        $groups = $this->get_group_options($coursecontext);
+        $mform->addElement('select', 'customint2', get_string('addgroup', 'enrol_cohort'), $groups);
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname" => value) of submitted data
+     * @param array $files array of uploaded files "element_name" => tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name" => "error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        global $DB;
+        $errors = array();
+
+        $params = array(
+            'roleid' => $data['roleid'],
+            'customint1' => $data['customint1'],
+            'courseid' => $data['courseid'],
+            'id' => $data['id']
+        );
+        $sql = "roleid = :roleid AND customint1 = :customint1 AND courseid = :courseid AND enrol = 'cohort' AND id <> :id";
+        if ($DB->record_exists_select('enrol', $sql, $params)) {
+            $errors['roleid'] = get_string('instanceexists', 'enrol_cohort');
+        }
+        $validstatus = array_keys($this->get_status_options());
+        $validcohorts = array_keys($this->get_cohort_options($instance, $context));
+        $validroles = array_keys($this->get_role_options($instance, $context));
+        $validgroups = array_keys($this->get_group_options($context));
+        $tovalidate = array(
+            'name' => PARAM_TEXT,
+            'status' => $validstatus,
+            'customint1' => $validcohorts,
+            'roleid' => $validroles,
+            'customint2' => $validgroups
+        );
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
 }
 
 /**
@@ -337,7 +518,9 @@ function enrol_cohort_allow_group_member_remove($itemid, $groupid, $userid) {
  * @return int $groupid Group ID for this cohort.
  */
 function enrol_cohort_create_new_group($courseid, $cohortid) {
-    global $DB;
+    global $DB, $CFG;
+
+    require_once($CFG->dirroot . '/group/lib.php');
 
     $groupname = $DB->get_field('cohort', 'name', array('id' => $cohortid), MUST_EXIST);
     $a = new stdClass();
similarity index 59%
rename from enrol/guest/edit.php
rename to enrol/editinstance.php
index 975f250..38d3ae7 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Edit instance of enrol_guest.
+ * Adds new instance of an enrolment plugin to specified course or edits current instance.
  *
- * Adds new instance of enrol_guest to specified course
- * or edits current instance.
- *
- * @package    enrol_guest
- * @copyright  2015 Andrew Hancox <andrewdchancox@googlemail.com>
+ * @package    core_enrol
+ * @copyright  2015 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-require('../../config.php');
+require('../config.php');
+require_once('editinstance_form.php');
 
 $courseid   = required_param('courseid', PARAM_INT);
+$type   = required_param('type', PARAM_COMPONENT);
 $instanceid = optional_param('id', 0, PARAM_INT);
 
 $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
 $context = context_course::instance($course->id, MUST_EXIST);
 
+$plugin = enrol_get_plugin($type);
+if (!$plugin) {
+    throw new moodle_exception('invaliddata', 'error');
+}
+
 require_login($course);
-require_capability('enrol/guest:config', $context);
+require_capability('enrol/' . $type . ':config', $context);
 
-$PAGE->set_url('/enrol/guest/edit.php', array('courseid' => $course->id, 'id' => $instanceid));
+$PAGE->set_url('/enrol/editinstance.php', array('courseid' => $course->id, 'id' => $instanceid, 'type' => $type));
 $PAGE->set_pagelayout('admin');
 
 $return = new moodle_url('/enrol/instances.php', array('id' => $course->id));
-if (!enrol_is_enabled('guest')) {
+if (!enrol_is_enabled($type)) {
     redirect($return);
 }
 
-$plugin = enrol_get_plugin('guest');
-
 if ($instanceid) {
-    $conditions = array('courseid' => $course->id, 'enrol' => 'guest', 'id' => $instanceid);
-    $instance = $DB->get_record('enrol', $conditions, '*', MUST_EXIST);
+    $instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => $type, 'id' => $instanceid), '*', MUST_EXIST);
+
 } else {
     require_capability('moodle/course:enrolconfig', $context);
     // No instance yet, we have to add new instance.
@@ -57,10 +59,10 @@ if ($instanceid) {
     $instance = (object)$plugin->get_instance_defaults();
     $instance->id       = null;
     $instance->courseid = $course->id;
+    $instance->status   = ENROL_INSTANCE_ENABLED; // Do not use default for automatically created instances here.
 }
 
-$mform = new \enrol_guest\enrol_guest_edit_form(null, array($instance, $plugin));
-$mform->set_data($instance);
+$mform = new enrol_instance_edit_form(null, array($instance, $plugin, $context, $type));
 
 if ($mform->is_cancelled()) {
     redirect($return);
@@ -68,22 +70,25 @@ if ($mform->is_cancelled()) {
 } else if ($data = $mform->get_data()) {
 
     if ($instance->id) {
-        $reset = ($instance->status != $data->status);
+        $reset = false;
+        if (isset($data->status)) {
+            $reset = ($instance->status != $data->status);
+        }
+
+        foreach ($data as $key => $value) {
+            $instance->$key = $value;
+        }
 
-        $instance->status         = $data->status;
-        $instance->password       = $data->password;
         $instance->timemodified   = time();
-        $DB->update_record('enrol', $instance);
+
+        $plugin->update_instance($instance, $data);
 
         if ($reset) {
             $context->mark_dirty();
         }
 
-        \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
     } else {
-        $fields = array(
-            'status'          => $data->status,
-            'password'        => $data->password);
+        $fields = (array) $data;
         $plugin->add_instance($course, $fields);
     }
 
@@ -91,9 +96,9 @@ if ($mform->is_cancelled()) {
 }
 
 $PAGE->set_heading($course->fullname);
-$PAGE->set_title(get_string('pluginname', 'enrol_guest'));
+$PAGE->set_title(get_string('pluginname', 'enrol_' . $type));
 
 echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('pluginname', 'enrol_guest'));
+echo $OUTPUT->heading(get_string('pluginname', 'enrol_' . $type));
 $mform->display();
 echo $OUTPUT->footer();
diff --git a/enrol/editinstance_form.php b/enrol/editinstance_form.php
new file mode 100644 (file)
index 0000000..728a7e8
--- /dev/null
@@ -0,0 +1,87 @@
+<?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/>.
+
+/**
+ * Adds new instance of enrol_plugin to specified course or edits current instance.
+ *
+ * @package    core_enrol
+ * @copyright  2015 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir.'/formslib.php');
+
+/**
+ * Standard edit form shared by all enrol plugins.
+ *
+ * @package    core_enrol
+ * @copyright  2015 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class enrol_instance_edit_form extends moodleform {
+
+    /**
+     * Called to define this moodle form
+     *
+     * @return void
+     */
+    public function definition() {
+        global $DB;
+
+        $mform = $this->_form;
+
+        list($instance, $plugin, $context, $type) = $this->_customdata;
+
+        $mform->addElement('header', 'header', get_string('pluginname', 'enrol_' . $type));
+
+        $plugin->edit_instance_form($instance, $mform, $context);
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+        $mform->addElement('hidden', 'courseid');
+        $mform->setType('courseid', PARAM_INT);
+
+        $mform->addElement('hidden', 'type');
+        $mform->setType('type', PARAM_COMPONENT);
+        $instance->type = $type;
+
+        $this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
+
+        $this->set_data($instance);
+    }
+
+    /**
+     * Validate this form. Calls plugin validation method.
+     *
+     * @param array $data
+     * @param array $files
+     * @return array
+     */
+    public function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        list($instance, $plugin, $context, $type) = $this->_customdata;
+
+        $pluginerrors = $plugin->edit_instance_validation($data, $files, $instance, $context);
+
+        $errors = array_merge($errors, $pluginerrors);
+
+        return $errors;
+    }
+
+}
diff --git a/enrol/guest/classes/enrol_guest_edit_form.php b/enrol/guest/classes/enrol_guest_edit_form.php
deleted file mode 100644 (file)
index 4eb6ef1..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Guest access plugin.
- *
- * Adds new instance of enrol_guest to specified course
- * or edits current instance.
- *
- * @package    enrol_guest
- * @copyright  2015 Andrew Hancox <andrewdchancox@googlemail.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-namespace enrol_guest;
-use moodleform;
-
-defined('MOODLE_INTERNAL') || die();
-
-require_once($CFG->libdir.'/formslib.php');
-
-/**
- * Class enrol_guest_edit_form
- * @copyright  2015 Andrew Hancox <andrewdchancox@googlemail.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class enrol_guest_edit_form extends moodleform {
-    /**
-     * Form definition
-     */
-    public function definition() {
-
-        $mform = $this->_form;
-
-        list($instance, $plugin) = $this->_customdata;
-
-        $mform->addElement('header', 'header', get_string('pluginname', 'enrol_guest'));
-
-        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
-                         ENROL_INSTANCE_DISABLED => get_string('no'));
-        $mform->addElement('select', 'status', get_string('status', 'enrol_guest'), $options);
-        $mform->addHelpButton('status', 'status', 'enrol_guest');
-        $mform->setDefault('status', $plugin->get_config('status'));
-        $mform->setAdvanced('status', $plugin->get_config('status_adv'));
-
-        $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_guest'));
-        $mform->addHelpButton('password', 'password', 'enrol_guest');
-
-        // If we have a new instance and the password is required - make sure it is set. For existing
-        // instances we do not force the password to be required as it may have been set to empty before
-        // the password was required. We check in the validation function whether this check is required
-        // for existing instances.
-        if (empty($instance->id) && $plugin->get_config('requirepassword')) {
-            $mform->addRule('password', get_string('required'), 'required', null);
-        }
-
-        $mform->addElement('hidden', 'id');
-        $mform->setType('id', PARAM_INT);
-        $mform->addElement('hidden', 'courseid');
-        $mform->setType('courseid', PARAM_INT);
-
-        $this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
-    }
-
-    /**
-     * Form validation
-     *
-     * @param array $data
-     * @param array $files
-     * @return array
-     */
-    public function validation($data, $files) {
-        $errors = parent::validation($data, $files);
-
-        list($instance, $plugin) = $this->_customdata;
-        $checkpassword = false;
-
-        if ($data['id']) {
-            // Check the password if we are enabling the plugin again.
-            if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
-                $checkpassword = true;
-            }
-
-            // Check the password if the instance is enabled and the password has changed.
-            if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
-                $checkpassword = true;
-            }
-        } else {
-            $checkpassword = true;
-        }
-
-        if ($checkpassword) {
-            $require = $plugin->get_config('requirepassword');
-            $policy  = $plugin->get_config('usepasswordpolicy');
-            if ($require && trim($data['password']) === '') {
-                $errors['password'] = get_string('required');
-            } else if (!empty($data['password']) && $policy) {
-                $errmsg = '';
-                if (!check_password_policy($data['password'], $errmsg)) {
-                    $errors['password'] = $errmsg;
-                }
-            }
-        }
-
-        return $errors;
-    }
-}
index 0c69877..696ae6b 100644 (file)
@@ -84,51 +84,6 @@ class enrol_guest_plugin extends enrol_plugin {
         return;
     }
 
-    /**
-     * Sets up navigation entries.
-     *
-     * @param stdClass $instancesnode
-     * @param stdClass $instance
-     * @return void
-     * @throws coding_exception
-     */
-    public function add_course_navigation($instancesnode, stdClass $instance) {
-        if ($instance->enrol !== 'guest') {
-             throw new coding_exception('Invalid enrol instance type!');
-        }
-
-        $context = context_course::instance($instance->courseid);
-        if (has_capability('enrol/guest:config', $context)) {
-            $managelink = new moodle_url('/enrol/guest/edit.php', array('courseid' => $instance->courseid, 'id' => $instance->id));
-            $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
-        }
-    }
-
-    /**
-     * Returns edit icons for the page with list of instances
-     * @param stdClass $instance
-     * @return array
-     * @throws coding_exception
-     */
-    public function get_action_icons(stdClass $instance) {
-        global $OUTPUT;
-
-        if ($instance->enrol !== 'guest') {
-            throw new coding_exception('invalid enrol instance!');
-        }
-        $context = context_course::instance($instance->courseid);
-
-        $icons = array();
-
-        if (has_capability('enrol/guest:config', $context)) {
-            $editlink = new moodle_url("/enrol/guest/edit.php", array('courseid' => $instance->courseid, 'id' => $instance->id));
-            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
-                array('class' => 'iconsmall')));
-        }
-
-        return $icons;
-    }
-
     /**
      * Attempt to automatically gain temporary guest access to course,
      * calling code has to make sure the plugin and instance are active.
@@ -161,24 +116,24 @@ class enrol_guest_plugin extends enrol_plugin {
     }
 
     /**
-     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * Returns true if the current user can add a new instance of enrolment plugin in course.
      * @param int $courseid
-     * @return moodle_url page url
+     * @return boolean
      */
-    public function get_newinstance_link($courseid) {
+    public function can_add_instance($courseid) {
         global $DB;
 
         $context = context_course::instance($courseid, MUST_EXIST);
 
         if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/guest:config', $context)) {
-            return NULL;
+            return false;
         }
 
         if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'guest'))) {
-            return NULL;
+            return false;
         }
 
-        return new moodle_url('/enrol/guest/edit.php', array('courseid' => $courseid));
+        return true;
     }
 
     /**
@@ -420,4 +375,108 @@ class enrol_guest_plugin extends enrol_plugin {
         }
         return $instanceinfo;
     }
+
+    /**
+     * Return an array of valid options for the status.
+     *
+     * @return array
+     */
+    protected function get_status_options() {
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $context
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
+        global $CFG;
+
+        $options = $this->get_status_options();
+        $mform->addElement('select', 'status', get_string('status', 'enrol_guest'), $options);
+        $mform->addHelpButton('status', 'status', 'enrol_guest');
+        $mform->setDefault('status', $this->get_config('status'));
+        $mform->setAdvanced('status', $this->get_config('status_adv'));
+
+        $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_guest'));
+        $mform->addHelpButton('password', 'password', 'enrol_guest');
+
+        // If we have a new instance and the password is required - make sure it is set. For existing
+        // instances we do not force the password to be required as it may have been set to empty before
+        // the password was required. We check in the validation function whether this check is required
+        // for existing instances.
+        if (empty($instance->id) && $this->get_config('requirepassword')) {
+            $mform->addRule('password', get_string('required'), 'required', null);
+        }
+    }
+
+    /**
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
+     */
+    public function use_standard_editing_ui() {
+        return true;
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        $errors = array();
+
+        $checkpassword = false;
+
+        if ($data['id']) {
+            // Check the password if we are enabling the plugin again.
+            if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
+                $checkpassword = true;
+            }
+
+            // Check the password if the instance is enabled and the password has changed.
+            if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
+                $checkpassword = true;
+            }
+        } else {
+            $checkpassword = true;
+        }
+
+        if ($checkpassword) {
+            $require = $this->get_config('requirepassword');
+            $policy  = $this->get_config('usepasswordpolicy');
+            if ($require && trim($data['password']) === '') {
+                $errors['password'] = get_string('required');
+            } else if (!empty($data['password']) && $policy) {
+                $errmsg = '';
+                if (!check_password_policy($data['password'], $errmsg)) {
+                    $errors['password'] = $errmsg;
+                }
+            }
+        }
+
+        $validstatus = array_keys($this->get_status_options());
+        $tovalidate = array(
+            'status' => $validstatus
+        );
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
+
+
 }
index 2fecf89..b1757a7 100644 (file)
@@ -71,4 +71,4 @@ class enrol_guest_enrol_form extends moodleform {
 
         return $errors;
     }
-}
\ No newline at end of file
+}
index ec95e75..52b5142 100644 (file)
@@ -277,10 +277,19 @@ echo html_writer::table($table);
 // access security is in each plugin
 $candidates = array();
 foreach (enrol_get_plugins(true) as $name=>$plugin) {
-    if (!$link = $plugin->get_newinstance_link($course->id)) {
-        continue;
+    if ($plugin->use_standard_editing_ui()) {
+        if ($plugin->can_add_instance($course->id)) {
+            // Standard add/edit UI.
+            $params = array('type' => $name, 'courseid' => $course->id);
+            $url = new moodle_url('/enrol/editinstance.php', $params);
+            $link = $url->out(false);
+            $candidates[$link] = get_string('pluginname', 'enrol_'.$name);
+        }
+    } else if ($url = $plugin->get_newinstance_link($course->id)) {
+        // Old custom UI.
+        $link = $url->out(false);
+        $candidates[$link] = get_string('pluginname', 'enrol_'.$name);
     }
-    $candidates[$link->out(false)] = get_string('pluginname', 'enrol_'.$name);
 }
 
 if ($candidates) {
diff --git a/enrol/manual/edit.php b/enrol/manual/edit.php
deleted file mode 100644 (file)
index 27e1de7..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_manual to specified course
- * or edits current instance.
- *
- * @package    enrol_manual
- * @copyright  2010 Petr Skoda  {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require('../../config.php');
-require_once('edit_form.php');
-
-$courseid = required_param('courseid', PARAM_INT);
-
-$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-$context = context_course::instance($course->id, MUST_EXIST);
-
-require_login($course);
-require_capability('enrol/manual:config', $context);
-
-$PAGE->set_url('/enrol/manual/edit.php', array('courseid'=>$course->id));
-$PAGE->set_pagelayout('admin');
-
-$return = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
-if (!enrol_is_enabled('manual')) {
-    redirect($return);
-}
-
-$plugin = enrol_get_plugin('manual');
-
-if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'), 'id ASC')) {
-    $instance = array_shift($instances);
-    if ($instances) {
-        // Oh - we allow only one instance per course!!
-        foreach ($instances as $del) {
-            $plugin->delete_instance($del);
-        }
-    }
-    // Merge these two settings to one value for the single selection element.
-    if ($instance->notifyall and $instance->expirynotify) {
-        $instance->expirynotify = 2;
-    }
-    unset($instance->notifyall);
-
-} else {
-    require_capability('moodle/course:enrolconfig', $context);
-    // No instance yet, we have to add new instance.
-    navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-    $instance = new stdClass();
-    $instance->id              = null;
-    $instance->courseid        = $course->id;
-    $instance->expirynotify    = $plugin->get_config('expirynotify');
-    $instance->expirythreshold = $plugin->get_config('expirythreshold');
-}
-
-$mform = new enrol_manual_edit_form(null, array($instance, $plugin, $context));
-
-if ($mform->is_cancelled()) {
-    redirect($return);
-
-} else if ($data = $mform->get_data()) {
-    if ($data->expirynotify == 2) {
-        $data->expirynotify = 1;
-        $data->notifyall = 1;
-    } else {
-        $data->notifyall = 0;
-    }
-    if (!$data->expirynotify) {
-        // Keep previous/default value of disabled expirythreshold option.
-        $data->expirythreshold = $instance->expirythreshold;
-    }
-    if ($instance->id) {
-        $instance->roleid          = $data->roleid;
-        $instance->enrolperiod     = $data->enrolperiod;
-        $instance->expirynotify    = $data->expirynotify;
-        $instance->notifyall       = $data->notifyall;
-        $instance->expirythreshold = $data->expirythreshold;
-        $instance->timemodified    = time();
-        $markdirty = ($instance->status != $data->status);
-        $instance->status = $data->status;
-
-        $DB->update_record('enrol', $instance);
-        \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
-
-        if ($markdirty) {
-            $context->mark_dirty();
-        }
-
-    } else {
-        $fields = array(
-            'status'          => $data->status,
-            'roleid'          => $data->roleid,
-            'enrolperiod'     => $data->enrolperiod,
-            'expirynotify'    => $data->expirynotify,
-            'notifyall'       => $data->notifyall,
-            'expirythreshold' => $data->expirythreshold);
-        $plugin->add_instance($course, $fields);
-    }
-
-    redirect($return);
-}
-
-$PAGE->set_title(get_string('pluginname', 'enrol_manual'));
-$PAGE->set_heading($course->fullname);
-
-echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('pluginname', 'enrol_manual'));
-$mform->display();
-echo $OUTPUT->footer();
diff --git a/enrol/manual/edit_form.php b/enrol/manual/edit_form.php
deleted file mode 100644 (file)
index bfc09a7..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_manual to specified course
- * or edits current instance.
- *
- * @package    enrol_manual
- * @copyright  2010 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.'/formslib.php');
-
-class enrol_manual_edit_form extends moodleform {
-
-    function definition() {
-        $mform = $this->_form;
-
-        list($instance, $plugin, $context) = $this->_customdata;
-
-        $mform->addElement('header', 'header', get_string('pluginname', 'enrol_manual'));
-
-        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
-                         ENROL_INSTANCE_DISABLED => get_string('no'));
-        $mform->addElement('select', 'status', get_string('status', 'enrol_manual'), $options);
-        $mform->addHelpButton('status', 'status', 'enrol_manual');
-        $mform->setDefault('status', $plugin->get_config('status'));
-
-        if ($instance->id) {
-            $roles = get_default_enrol_roles($context, $instance->roleid);
-        } else {
-            $roles = get_default_enrol_roles($context, $plugin->get_config('roleid'));
-        }
-        $mform->addElement('select', 'roleid', get_string('defaultrole', 'role'), $roles);
-        $mform->setDefault('roleid', $plugin->get_config('roleid'));
-
-        $mform->addElement('duration', 'enrolperiod', get_string('defaultperiod', 'enrol_manual'), array('optional' => true, 'defaultunit' => 86400));
-        $mform->setDefault('enrolperiod', $plugin->get_config('enrolperiod'));
-        $mform->addHelpButton('enrolperiod', 'defaultperiod', 'enrol_manual');
-
-        $options = array(0 => get_string('no'), 1 => get_string('expirynotifyenroller', 'core_enrol'), 2 => get_string('expirynotifyall', 'core_enrol'));
-        $mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options);
-        $mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol');
-
-        $mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), array('optional' => false, 'defaultunit' => 86400));
-        $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol');
-        $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0);
-
-        $mform->addElement('hidden', 'courseid');
-        $mform->setType('courseid', PARAM_INT);
-
-        if (enrol_accessing_via_instance($instance)) {
-            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), get_string('instanceeditselfwarningtext', 'core_enrol'));
-        }
-
-        $this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
-
-        $this->set_data($instance);
-    }
-
-    function validation($data, $files) {
-        global $DB;
-
-        $errors = parent::validation($data, $files);
-
-        if ($data['expirynotify'] > 0 and $data['expirythreshold'] < 86400) {
-            $errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol');
-        }
-
-        return $errors;
-    }
-}
index b50e3cc..445b769 100644 (file)
@@ -78,24 +78,18 @@ class enrol_manual_plugin extends enrol_plugin {
     }
 
     /**
-     * Returns enrolment instance manage link.
+     * Return true if we can add a new instance to this course.
      *
-     * By defaults looks for manage.php file and tests for manage capability.
-     *
-     * @param navigation_node $instancesnode
-     * @param stdClass $instance
-     * @return moodle_url;
+     * @param int $courseid
+     * @return boolean
      */
-    public function add_course_navigation($instancesnode, stdClass $instance) {
-        if ($instance->enrol !== 'manual') {
-             throw new coding_exception('Invalid enrol instance type!');
-        }
-
-        $context = context_course::instance($instance->courseid);
-        if (has_capability('enrol/manual:config', $context)) {
-            $managelink = new moodle_url('/enrol/manual/edit.php', array('courseid'=>$instance->courseid));
-            $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
+    public function can_add_instance($courseid) {
+        $context = context_course::instance($courseid, MUST_EXIST);
+        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/manual:config', $context)) {
+            return false;
         }
+        // Multiple instances supported - multiple parent courses linked.
+        return true;
     }
 
     /**
@@ -106,47 +100,18 @@ class enrol_manual_plugin extends enrol_plugin {
     public function get_action_icons(stdClass $instance) {
         global $OUTPUT;
 
-        if ($instance->enrol !== 'manual') {
-            throw new coding_exception('invalid enrol instance!');
-        }
         $context = context_course::instance($instance->courseid);
 
         $icons = array();
-
         if (has_capability('enrol/manual:enrol', $context) or has_capability('enrol/manual:unenrol', $context)) {
             $managelink = new moodle_url("/enrol/manual/manage.php", array('enrolid'=>$instance->id));
             $icons[] = $OUTPUT->action_icon($managelink, new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', array('class'=>'iconsmall')));
         }
-        if (has_capability('enrol/manual:config', $context)) {
-            $editlink = new moodle_url("/enrol/manual/edit.php", array('courseid'=>$instance->courseid));
-            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
-                    array('class' => 'iconsmall')));
-        }
+        $icons = $icons + parent::get_action_icons($instance);
 
         return $icons;
     }
 
-    /**
-     * Returns link to page which may be used to add new instance of enrolment plugin in course.
-     * @param int $courseid
-     * @return moodle_url page url
-     */
-    public function get_newinstance_link($courseid) {
-        global $DB;
-
-        $context = context_course::instance($courseid, MUST_EXIST);
-
-        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/manual:config', $context)) {
-            return NULL;
-        }
-
-        if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'manual'))) {
-            return NULL;
-        }
-
-        return new moodle_url('/enrol/manual/edit.php', array('courseid'=>$courseid));
-    }
-
     /**
      * Add new instance of enrol plugin with default settings.
      * @param stdClass $course
@@ -188,6 +153,26 @@ class enrol_manual_plugin extends enrol_plugin {
         return parent::add_instance($course, $fields);
     }
 
+    /**
+     * Update instance of enrol plugin.
+     * @param stdClass $instance
+     * @param stdClass $data modified instance fields
+     * @return boolean
+     */
+    public function update_instance($instance, $data) {
+        global $DB;
+
+        // Delete all other instances, leaving only one.
+        if ($instances = $DB->get_records('enrol', array('courseid' => $instance->courseid, 'enrol' => 'manual'), 'id ASC')) {
+            foreach ($instances as $anotherinstance) {
+                if ($anotherinstance->id != $instance->id) {
+                    $this->delete_instance($anotherinstance);
+                }
+            }
+        }
+        return parent::update_instance($instance, $data);
+    }
+
     /**
      * Returns a button to manually enrol users through the manual enrolment plugin.
      *
@@ -610,4 +595,130 @@ class enrol_manual_plugin extends enrol_plugin {
             $this->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades);
         }
     }
+
+    /**
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
+     */
+    public function use_standard_editing_ui() {
+        return true;
+    }
+
+    /**
+     * Return an array of valid options for the status.
+     *
+     * @return array
+     */
+    protected function get_status_options() {
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the roleid.
+     *
+     * @param stdClass $instance
+     * @param context $context
+     * @return array
+     */
+    protected function get_roleid_options($instance, $context) {
+        if ($instance->id) {
+            $roles = get_default_enrol_roles($context, $instance->roleid);
+        } else {
+            $roles = get_default_enrol_roles($context, $this->get_config('roleid'));
+        }
+        return $roles;
+    }
+
+    /**
+     * Return an array of valid options for the expirynotify.
+     *
+     * @return array
+     */
+    protected function get_expirynotify_options() {
+        $options = array(
+            0 => get_string('no'),
+            1 => get_string('expirynotifyenroller', 'core_enrol'),
+            2 => get_string('expirynotifyall', 'core_enrol')
+        );
+        return $options;
+    }
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $context
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
+
+        $options = $this->get_status_options();
+        $mform->addElement('select', 'status', get_string('status', 'enrol_manual'), $options);
+        $mform->addHelpButton('status', 'status', 'enrol_manual');
+        $mform->setDefault('status', $this->get_config('status'));
+
+        $roles = $this->get_roleid_options($instance, $context);
+        $mform->addElement('select', 'roleid', get_string('defaultrole', 'role'), $roles);
+        $mform->setDefault('roleid', $this->get_config('roleid'));
+
+        $options = array('optional' => true, 'defaultunit' => 86400);
+        $mform->addElement('duration', 'enrolperiod', get_string('defaultperiod', 'enrol_manual'), $options);
+        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
+        $mform->addHelpButton('enrolperiod', 'defaultperiod', 'enrol_manual');
+
+        $options = $this->get_expirynotify_options();
+        $mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options);
+        $mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol');
+
+        $options = array('optional' => false, 'defaultunit' => 86400);
+        $mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), $options);
+        $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol');
+        $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0);
+
+        if (enrol_accessing_via_instance($instance)) {
+            $warntext = get_string('instanceeditselfwarningtext', 'core_enrol');
+            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warntext);
+        }
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        $errors = array();
+
+        if ($data['expirynotify'] > 0 and $data['expirythreshold'] < 86400) {
+            $errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol');
+        }
+
+        $validstatus = array_keys($this->get_status_options());
+        $validroles = array_keys($this->get_roleid_options($instance, $context));
+        $validexpirynotify = array_keys($this->get_expirynotify_options());
+
+        $tovalidate = array(
+            'status' => $validstatus,
+            'roleid' => $validroles,
+            'enrolperiod' => PARAM_INT,
+            'expirynotify' => $validexpirynotify,
+            'expirythreshold' => PARAM_INT
+        );
+
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
+
 }
diff --git a/enrol/meta/addinstance.php b/enrol/meta/addinstance.php
deleted file mode 100644 (file)
index aea6adb..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_meta to specified course.
- *
- * @package    enrol_meta
- * @copyright  2010 Petr Skoda {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require('../../config.php');
-require_once("$CFG->dirroot/enrol/meta/addinstance_form.php");
-require_once("$CFG->dirroot/enrol/meta/locallib.php");
-
-$id = required_param('id', PARAM_INT); // course id
-$message = optional_param('message', null, PARAM_TEXT);
-$instanceid = optional_param('enrolid', 0, PARAM_INT);
-
-$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
-$context = context_course::instance($course->id, MUST_EXIST);
-
-$PAGE->set_url('/enrol/meta/addinstance.php', array('id'=>$course->id));
-$PAGE->set_pagelayout('admin');
-
-navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-
-require_login($course);
-require_capability('moodle/course:enrolconfig', $context);
-
-$enrol = enrol_get_plugin('meta');
-if ($instanceid) {
-    require_capability('enrol/meta:config', $context);
-    $instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'meta',
-        'id' => $instanceid), '*', MUST_EXIST);
-
-} else {
-    if (!$enrol->get_newinstance_link($course->id)) {
-        redirect(new moodle_url('/enrol/instances.php', array('id' => $course->id)));
-    }
-    $instance = null;
-}
-
-$mform = new enrol_meta_addinstance_form(null, array('course' => $course, 'instance' => $instance));
-
-if ($mform->is_cancelled()) {
-    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-
-} else if ($data = $mform->get_data()) {
-    if (!empty($data->customint2) && $data->customint2 == ENROL_META_CREATE_GROUP) {
-        $data->customint2 = enrol_meta_create_new_group($course->id, $data->link);
-    }
-    if ($instance) {
-        if ($data->customint2 != $instance->customint2) {
-            $DB->update_record('enrol', array('id' => $instance->id, 'customint2' => $data->customint2));
-            enrol_meta_sync($course->id);
-        }
-    } else {
-        $eid = $enrol->add_instance($course, array('customint1' => $data->link,
-                                               'customint2' => $data->customint2));
-        enrol_meta_sync($course->id);
-        if (!empty($data->submitbuttonnext)) {
-            redirect(new moodle_url('/enrol/meta/addinstance.php',
-                    array('id' => $course->id, 'message' => 'added')));
-        }
-    }
-    redirect(new moodle_url('/enrol/instances.php', array('id' => $course->id)));
-}
-
-$PAGE->set_heading($course->fullname);
-$PAGE->set_title(get_string('pluginname', 'enrol_meta'));
-
-echo $OUTPUT->header();
-
-if ($message === 'added') {
-    echo $OUTPUT->notification(get_string('instanceadded', 'enrol'), 'notifysuccess');
-}
-
-$mform->display();
-
-echo $OUTPUT->footer();
diff --git a/enrol/meta/addinstance_form.php b/enrol/meta/addinstance_form.php
deleted file mode 100644 (file)
index c28a762..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds instance form
- *
- * @package    enrol_meta
- * @copyright  2010 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/formslib.php");
-
-class enrol_meta_addinstance_form extends moodleform {
-    protected $course;
-
-    function definition() {
-        global $CFG, $DB;
-
-        $mform  = $this->_form;
-        $course = $this->_customdata['course'];
-        $instance = $this->_customdata['instance'];
-        $this->course = $course;
-
-        if ($instance) {
-            $where = 'WHERE c.id = :courseid';
-            $params = array('courseid' => $instance->customint1);
-            $existing = array();
-        } else {
-            $where = '';
-            $params = array();
-            $existing = $DB->get_records('enrol', array('enrol' => 'meta', 'courseid' => $course->id), '', 'customint1, id');
-        }
-
-        // TODO: this has to be done via ajax or else it will fail very badly on large sites!
-        $courses = array('' => get_string('choosedots'));
-        $select = ', ' . context_helper::get_preload_record_columns_sql('ctx');
-        $join = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
-
-        $plugin = enrol_get_plugin('meta');
-        $sortorder = 'c.' . $plugin->get_config('coursesort', 'sortorder') . ' ASC';
-
-        $sql = "SELECT c.id, c.fullname, c.shortname, c.visible $select FROM {course} c $join $where ORDER BY $sortorder";
-        $rs = $DB->get_recordset_sql($sql, array('contextlevel' => CONTEXT_COURSE) + $params);
-        foreach ($rs as $c) {
-            if ($c->id == SITEID or $c->id == $course->id or isset($existing[$c->id])) {
-                continue;
-            }
-            context_helper::preload_from_record($c);
-            $coursecontext = context_course::instance($c->id);
-            if (!$c->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
-                continue;
-            }
-            if (!has_capability('enrol/meta:selectaslinked', $coursecontext)) {
-                continue;
-            }
-            $courses[$c->id] = $coursecontext->get_context_name(false);
-        }
-        $rs->close();
-
-        $groups = array(0 => get_string('none'));
-        if (has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
-            $groups[ENROL_META_CREATE_GROUP] = get_string('creategroup', 'enrol_meta');
-        }
-        foreach (groups_get_all_groups($course->id) as $group) {
-            $groups[$group->id] = format_string($group->name, true, array('context' => context_course::instance($course->id)));
-        }
-
-        $mform->addElement('header','general', get_string('pluginname', 'enrol_meta'));
-
-        $mform->addElement('select', 'link', get_string('linkedcourse', 'enrol_meta'), $courses);
-        $mform->addRule('link', get_string('required'), 'required', null, 'client');
-
-        $mform->addElement('select', 'customint2', get_string('addgroup', 'enrol_meta'), $groups);
-
-        $mform->addElement('hidden', 'id', null);
-        $mform->setType('id', PARAM_INT);
-
-        $mform->addElement('hidden', 'enrolid');
-        $mform->setType('enrolid', PARAM_INT);
-
-        $data = array('id' => $course->id);
-        if ($instance) {
-            $data['link'] = $instance->customint1;
-            $data['enrolid'] = $instance->id;
-            $data['customint2'] = $instance->customint2;
-            $mform->freeze('link');
-            $this->add_action_buttons();
-        } else {
-            $this->add_add_buttons();
-        }
-        $this->set_data($data);
-    }
-
-    /**
-     * Adds buttons on create new method form
-     */
-    protected function add_add_buttons() {
-        $mform = $this->_form;
-        $buttonarray = array();
-        $buttonarray[0] = $mform->createElement('submit', 'submitbutton', get_string('addinstance', 'enrol'));
-        $buttonarray[1] = $mform->createElement('submit', 'submitbuttonnext', get_string('addinstanceanother', 'enrol'));
-        $buttonarray[2] = $mform->createElement('cancel');
-        $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
-        $mform->closeHeaderBefore('buttonar');
-    }
-
-    function validation($data, $files) {
-        global $DB, $CFG;
-
-        $errors = parent::validation($data, $files);
-
-        if ($this->_customdata['instance']) {
-            // Nothing to validate in case of editing.
-            return $errors;
-        }
-
-        // TODO: this is duplicated here because it may be necessary once we implement ajax course selection element
-
-        if (!$c = $DB->get_record('course', array('id'=>$data['link']))) {
-            $errors['link'] = get_string('required');
-        } else {
-            $coursecontext = context_course::instance($c->id);
-            $existing = $DB->get_records('enrol', array('enrol'=>'meta', 'courseid'=>$this->course->id), '', 'customint1, id');
-            if (!$c->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
-                $errors['link'] = get_string('error');
-            } else if (!has_capability('enrol/meta:selectaslinked', $coursecontext)) {
-                $errors['link'] = get_string('error');
-            } else if ($c->id == SITEID or $c->id == $this->course->id or isset($existing[$c->id])) {
-                $errors['link'] = get_string('error');
-            }
-        }
-
-        return $errors;
-    }
-}
-
index 538fd1f..5659d9f 100644 (file)
@@ -64,17 +64,18 @@ class enrol_meta_plugin extends enrol_plugin {
     }
 
     /**
-     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * Returns true if we can add a new instance to this course.
+     *
      * @param int $courseid
-     * @return moodle_url page url
+     * @return boolean
      */
-    public function get_newinstance_link($courseid) {
+    public function can_add_instance($courseid) {
         $context = context_course::instance($courseid, MUST_EXIST);
         if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/meta:config', $context)) {
-            return NULL;
+            return false;
         }
-        // multiple instances supported - multiple parent courses linked
-        return new moodle_url('/enrol/meta/addinstance.php', array('id'=>$courseid));
+        // Multiple instances supported - multiple parent courses linked.
+        return true;
     }
 
     /**
@@ -127,6 +128,56 @@ class enrol_meta_plugin extends enrol_plugin {
         // We should probably add some sync button to the course enrol methods overview page.
     }
 
+    /**
+     * Add new instance of enrol plugin.
+     * @param object $course
+     * @param array $fields instance fields
+     * @return int id of new instance, null if can not be created
+     */
+    public function add_instance($course, array $fields = null) {
+        global $CFG;
+
+        require_once("$CFG->dirroot/enrol/meta/locallib.php");
+
+        if (!empty($fields['customint2']) && $fields['customint2'] == ENROL_META_CREATE_GROUP) {
+            $context = context_course::instance($course->id);
+            require_capability('moodle/course:managegroups', $context);
+            $groupid = enrol_meta_create_new_group($course->id, $fields['customint1']);
+            $fields['customint2'] = $groupid;
+        }
+
+        $result = parent::add_instance($course, $fields);
+
+        enrol_meta_sync($course->id);
+
+        return $result;
+    }
+
+    /**
+     * Update instance of enrol plugin.
+     * @param stdClass $instance
+     * @param stdClass $data modified instance fields
+     * @return boolean
+     */
+    public function update_instance($instance, $data) {
+        global $CFG;
+
+        require_once("$CFG->dirroot/enrol/meta/locallib.php");
+
+        if (!empty($data->customint2) && $data->customint2 == ENROL_META_CREATE_GROUP) {
+            $context = context_course::instance($instance->courseid);
+            require_capability('moodle/course:managegroups', $context);
+            $groupid = enrol_meta_create_new_group($instance->courseid, $data->customint1);
+            $data->customint2 = $groupid;
+        }
+
+        $result = parent::update_instance($instance, $data);
+
+        enrol_meta_sync($instance->courseid);
+
+        return $result;
+    }
+
     /**
      * Update instance status
      *
@@ -176,6 +227,153 @@ class enrol_meta_plugin extends enrol_plugin {
         return has_capability('enrol/meta:config', $context);
     }
 
+    /**
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
+     */
+    public function use_standard_editing_ui() {
+        return true;
+    }
+
+    /**
+     * Return an array of valid options for the courses.
+     *
+     * @param stdClass $instance
+     * @param context $coursecontext
+     * @return array
+     */
+    protected function get_course_options($instance, $coursecontext) {
+        global $DB;
+
+        if ($instance->id) {
+            $where = 'WHERE c.id = :courseid';
+            $params = array('courseid' => $instance->customint1);
+            $existing = array();
+        } else {
+            $where = '';
+            $params = array();
+            $instanceparams = array('enrol' => 'meta', 'courseid' => $instance->courseid);
+            $existing = $DB->get_records('enrol', $instanceparams, '', 'customint1, id');
+        }
+
+        // TODO: this has to be done via ajax or else it will fail very badly on large sites!
+        $courses = array('' => get_string('choosedots'));
+        $select = ', ' . context_helper::get_preload_record_columns_sql('ctx');
+        $join = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
+
+        $sortorder = 'c.' . $this->get_config('coursesort', 'sortorder') . ' ASC';
+
+        $sql = "SELECT c.id, c.fullname, c.shortname, c.visible $select FROM {course} c $join $where ORDER BY $sortorder";
+        $rs = $DB->get_recordset_sql($sql, array('contextlevel' => CONTEXT_COURSE) + $params);
+        foreach ($rs as $c) {
+            if ($c->id == SITEID or $c->id == $instance->courseid or isset($existing[$c->id])) {
+                continue;
+            }
+            context_helper::preload_from_record($c);
+            $coursecontext = context_course::instance($c->id);
+            if (!$c->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                continue;
+            }
+            if (!has_capability('enrol/meta:selectaslinked', $coursecontext)) {
+                continue;
+            }
+            $courses[$c->id] = $coursecontext->get_context_name(false);
+        }
+        $rs->close();
+        return $courses;
+    }
+
+    /**
+     * Return an array of valid options for the groups.
+     *
+     * @param context $coursecontext
+     * @return array
+     */
+    protected function get_group_options($coursecontext) {
+        $groups = array(0 => get_string('none'));
+        $courseid = $coursecontext->instanceid;
+        if (has_capability('moodle/course:managegroups', $coursecontext)) {
+            $groups[ENROL_META_CREATE_GROUP] = get_string('creategroup', 'enrol_meta');
+        }
+        foreach (groups_get_all_groups($courseid) as $group) {
+            $groups[$group->id] = format_string($group->name, true, array('context' => $coursecontext));
+        }
+        return $groups;
+    }
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $coursecontext
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $coursecontext) {
+        global $DB;
+
+        $courses = $this->get_course_options($instance, $coursecontext);
+        $groups = $this->get_group_options($coursecontext);
+
+        $mform->addElement('select', 'customint1', get_string('linkedcourse', 'enrol_meta'), $courses);
+        $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
+        if (!empty($instance->id)) {
+            $mform->freeze('customint1');
+        }
+
+        $mform->addElement('select', 'customint2', get_string('addgroup', 'enrol_meta'), $groups);
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        global $DB;
+        $errors = array();
+        $thiscourseid = $context->instanceid;
+        $c = false;
+
+        if (!empty($data['customint1'])) {
+            $c = $DB->get_record('course', array('id' => $data['customint1']));
+        }
+
+        if (!$c) {
+            $errors['customint1'] = get_string('required');
+        } else {
+            $coursecontext = context_course::instance($c->id);
+            $existing = $DB->get_records('enrol', array('enrol' => 'meta', 'courseid' => $thiscourseid), '', 'customint1, id');
+            if (!$c->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                $errors['customint1'] = get_string('error');
+            } else if (!has_capability('enrol/meta:selectaslinked', $coursecontext)) {
+                $errors['customint1'] = get_string('error');
+            } else if ($c->id == SITEID or $c->id == $thiscourseid or isset($existing[$c->id])) {
+                $errors['customint1'] = get_string('error');
+            }
+        }
+
+        $validcourses = array_keys($this->get_course_options($instance, $context));
+        $validgroups = array_keys($this->get_group_options($context));
+
+        $tovalidate = array(
+            'customint1' => $validcourses,
+            'customint2' => $validgroups
+        );
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
+
+
     /**
      * Restore instance and map settings.
      *
@@ -254,28 +452,4 @@ class enrol_meta_plugin extends enrol_plugin {
         return;
     }
 
-    /**
-     * Returns edit icons for the page with list of instances.
-     * @param stdClass $instance
-     * @return array
-     */
-    public function get_action_icons(stdClass $instance) {
-        global $OUTPUT;
-
-        if ($instance->enrol !== 'meta') {
-            throw new coding_exception('invalid enrol instance!');
-        }
-        $context = context_course::instance($instance->courseid);
-
-        $icons = array();
-
-        if (has_capability('enrol/meta:config', $context)) {
-            $editlink = new moodle_url("/enrol/meta/addinstance.php",
-                array('id' => $instance->courseid, 'enrolid' => $instance->id));
-            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
-                array('class' => 'iconsmall')));
-        }
-
-        return $icons;
-    }
 }
index 6566759..9a07371 100644 (file)
@@ -46,12 +46,13 @@ Feature: Enrolments are synchronised with meta courses
   Scenario: Add meta enrolment instance with groups
     When I follow "Course 3"
     And I navigate to "Enrolment methods" node in "Course administration > Users"
-    And I set the field "Add method" to "Course meta link"
-    And I press "Go"
+    And I select "Course meta link" from the "Add method" singleselect
     And I set the following fields to these values:
       | Link course  | Course 1      |
       | Add to group | Groupcourse 1 |
-    And I press "Add method and create another"
+    And I press "Add method"
+    And I set the field "Add method" to "Course meta link"
+    And I press "Go"
     And I set the following fields to these values:
       | Link course  | Course 2      |
       | Add to group | Groupcourse 2 |
@@ -91,7 +92,8 @@ Feature: Enrolments are synchronised with meta courses
     And I set the following fields to these values:
       | Link course  | Course 1      |
       | Add to group | Groupcourse 1 |
-    And I press "Add method and create another"
+    And I press "Add method"
+    And I select "Course meta link" from the "Add method" singleselect
     And I set the following fields to these values:
       | Link course  | Course 2      |
     And I press "Add method"
index 9f0fb8a..5f292e5 100644 (file)
@@ -128,12 +128,15 @@ class enrol_meta_plugin_testcase extends advanced_testcase {
         $this->assertEquals(7, $DB->count_records('user_enrolments'));
         $this->assertEquals(6, $DB->count_records('role_assignments'));
 
+        // Disable the plugin to prevent add_instance from calling enrol_meta_sync.
+        $this->disable_plugin();
         $e1 = $metalplugin->add_instance($course3, array('customint1'=>$course1->id));
         $e2 = $metalplugin->add_instance($course3, array('customint1'=>$course2->id));
         $e3 = $metalplugin->add_instance($course4, array('customint1'=>$course2->id));
         $enrol1 = $DB->get_record('enrol', array('id'=>$e1));
         $enrol2 = $DB->get_record('enrol', array('id'=>$e2));
         $enrol3 = $DB->get_record('enrol', array('id'=>$e3));
+        $this->enable_plugin();
 
         enrol_meta_sync($course4->id, false);
         $this->assertEquals(9, $DB->count_records('user_enrolments'));
diff --git a/enrol/mnet/addinstance.php b/enrol/mnet/addinstance.php
deleted file mode 100644 (file)
index 9678b40..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_mnet into the specified course
- *
- * @package    enrol_mnet
- * @copyright  2010 David Mudrak <david@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require(dirname(dirname(dirname(__FILE__))).'/config.php');
-require_once($CFG->dirroot.'/enrol/mnet/addinstance_form.php');
-require_once($CFG->dirroot.'/mnet/service/enrol/locallib.php');
-
-$id = required_param('id', PARAM_INT); // course id
-
-$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
-$context = context_course::instance($course->id, MUST_EXIST);
-
-require_login($course);
-require_capability('moodle/course:enrolconfig', $context);
-
-$PAGE->set_url('/enrol/mnet/addinstance.php', array('id'=>$course->id));
-$PAGE->set_pagelayout('standard');
-
-// Try and make the manage instances node on the navigation active
-$courseadmin = $PAGE->settingsnav->get('courseadmin');
-if ($courseadmin && $courseadmin->get('users') && $courseadmin->get('users')->get('manageinstances')) {
-    $courseadmin->get('users')->get('manageinstances')->make_active();
-}
-
-$enrol = enrol_get_plugin('mnet');
-// make sure we were allowed to get here form the Enrolment methods page
-if (!$enrol->get_newinstance_link($course->id)) {
-    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-}
-$service = mnetservice_enrol::get_instance();
-$mform = new enrol_mnet_addinstance_form(null, array('course'=>$course, 'enrol'=>$enrol, 'service'=>$service));
-
-if ($mform->is_cancelled()) {
-    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-
-} else if ($data = $mform->get_data()) {
-    $enrol->add_instance($course, array('customint1'=>$data->hostid, 'roleid'=>$data->roleid, 'name'=>$data->name));
-    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-}
-
-$PAGE->set_heading($course->fullname);
-$PAGE->set_title(get_string('pluginname', 'enrol_mnet'));
-
-echo $OUTPUT->header();
-$mform->display();
-echo $OUTPUT->footer();
diff --git a/enrol/mnet/addinstance_form.php b/enrol/mnet/addinstance_form.php
deleted file mode 100644 (file)
index 436e92b..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Form to add an instance of enrol_mnet plugin
- *
- * @package    enrol_mnet
- * @copyright  2010 David Mudrak <david@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-require_once("$CFG->libdir/formslib.php");
-
-class enrol_mnet_addinstance_form extends moodleform {
-    function definition() {
-        global $CFG, $DB;
-
-        $mform   = $this->_form;
-        $course  = $this->_customdata['course'];
-        $enrol   = $this->_customdata['enrol'];
-        $service = $this->_customdata['service'];
-        $coursecontext = context_course::instance($course->id);
-
-        $subscribers = $service->get_remote_subscribers();
-        $hosts = array(0 => get_string('remotesubscribersall', 'enrol_mnet'));
-        foreach ($subscribers as $hostid => $subscriber) {
-            $hosts[$hostid] = $subscriber->appname.': '.$subscriber->hostname.' ('.$subscriber->hosturl.')';
-        }
-        $roles = get_assignable_roles($coursecontext);
-
-        $mform->addElement('header','general', get_string('pluginname', 'enrol_mnet'));
-
-        $mform->addElement('select', 'hostid', get_string('remotesubscriber', 'enrol_mnet'), $hosts);
-        $mform->addHelpButton('hostid', 'remotesubscriber', 'enrol_mnet');
-        $mform->addRule('hostid', get_string('required'), 'required', null, 'client');
-
-        $mform->addElement('select', 'roleid', get_string('roleforremoteusers', 'enrol_mnet'), $roles);
-        $mform->addHelpButton('roleid', 'roleforremoteusers', 'enrol_mnet');
-        $mform->addRule('roleid', get_string('required'), 'required', null, 'client');
-        $mform->setDefault('roleid', $enrol->get_config('roleid'));
-
-        $mform->addElement('text', 'name', get_string('instancename', 'enrol_mnet'));
-        $mform->addHelpButton('name', 'instancename', 'enrol_mnet');
-        $mform->setType('name', PARAM_TEXT);
-
-        $mform->addElement('hidden', 'id', null);
-        $mform->setType('id', PARAM_INT);
-
-        $this->add_action_buttons();
-
-        $this->set_data(array('id'=>$course->id));
-    }
-
-    /**
-     * Do not allow multiple instances for single remote host
-     *
-     * @param array $data raw form data
-     * @param array $files
-     * @return array of errors
-     */
-    function validation($data, $files) {
-        global $DB;
-
-        $errors = array();
-
-        if ($DB->record_exists('enrol', array('enrol' => 'mnet', 'courseid' => $data['id'], 'customint1' => $data['hostid']))) {
-            $errors['hostid'] = get_string('error_multiplehost', 'enrol_mnet');
-        }
-
-        return $errors;
-    }
-}
index 419228b..bc33fe4 100644 (file)
@@ -62,31 +62,31 @@ class enrol_mnet_plugin extends enrol_plugin {
     }
 
     /**
-     * Returns link to page which may be used to add new instance of enrolment plugin into the course
+     * Returns true if a new instance can be added to this course.
      *
      * The link is returned only if there are some MNet peers that we publish enrolment service to.
      *
      * @param int $courseid id of the course to add the instance to
-     * @return moodle_url|null page url or null if instance can not be created
+     * @return boolean
      */
-    public function get_newinstance_link($courseid) {
+    public function can_add_instance($courseid) {
         global $CFG, $DB;
         require_once($CFG->dirroot.'/mnet/service/enrol/locallib.php');
 
         $service = mnetservice_enrol::get_instance();
         if (!$service->is_available()) {
-            return null;
+            return false;
         }
         $coursecontext = context_course::instance($courseid);
         if (!has_capability('moodle/course:enrolconfig', $coursecontext)) {
-            return null;
+            return false;
         }
         $subscribers = $service->get_remote_subscribers();
         if (empty($subscribers)) {
-            return null;
+            return false;
         }
 
-        return new moodle_url('/enrol/mnet/addinstance.php', array('id'=>$courseid));
+        return true;
     }
 
     /**
@@ -110,4 +110,104 @@ class enrol_mnet_plugin extends enrol_plugin {
         $context = context_course::instance($instance->courseid);
         return has_capability('enrol/mnet:config', $context);
     }
+
+    /**
+     * Return an array of valid options for the hosts property.
+     *
+     * @return array
+     */
+    protected function get_valid_hosts_options() {
+        global $CFG;
+        require_once($CFG->dirroot.'/mnet/service/enrol/locallib.php');
+
+        $service = mnetservice_enrol::get_instance();
+
+        $subscribers = $service->get_remote_subscribers();
+        $hosts = array(0 => get_string('remotesubscribersall', 'enrol_mnet'));
+        foreach ($subscribers as $hostid => $subscriber) {
+            $hosts[$hostid] = $subscriber->appname.': '.$subscriber->hostname.' ('.$subscriber->hosturl.')';
+        }
+        return $hosts;
+    }
+
+    /**
+     * Return an array of valid options for the roles property.
+     *
+     * @param context $context
+     * @return array
+     */
+    protected function get_valid_roles_options($context) {
+        $roles = get_assignable_roles($context);
+        return $roles;
+    }
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $context
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
+        global $CFG;
+
+        $hosts = $this->get_valid_hosts_options();
+        $mform->addElement('select', 'customint1', get_string('remotesubscriber', 'enrol_mnet'), $hosts);
+        $mform->addHelpButton('customint1', 'remotesubscriber', 'enrol_mnet');
+        $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
+
+        $roles = $this->get_valid_roles_options($context);
+        $mform->addElement('select', 'roleid', get_string('roleforremoteusers', 'enrol_mnet'), $roles);
+        $mform->addHelpButton('roleid', 'roleforremoteusers', 'enrol_mnet');
+        $mform->addRule('roleid', get_string('required'), 'required', null, 'client');
+        $mform->setDefault('roleid', $this->get_config('roleid'));
+
+        $mform->addElement('text', 'name', get_string('instancename', 'enrol_mnet'));
+        $mform->addHelpButton('name', 'instancename', 'enrol_mnet');
+        $mform->setType('name', PARAM_TEXT);
+    }
+
+    /**
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
+     */
+    public function use_standard_editing_ui() {
+        return true;
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        global $DB;
+        $errors = array();
+
+        $validroles = array_keys($this->get_valid_roles_options($context));
+        $validhosts = array_keys($this->get_valid_hosts_options());
+
+        $params = array('enrol' => 'mnet', 'courseid' => $instance->courseid, 'customint1' => $data['customint1']);
+        if ($DB->record_exists('enrol', $params)) {
+            $errors['customint1'] = get_string('error_multiplehost', 'enrol_mnet');
+        }
+
+        $tovalidate = array(
+            'customint1' => $validhosts,
+            'roleid' => $validroles,
+            'name' => PARAM_TEXT
+        );
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
 }
diff --git a/enrol/paypal/edit.php b/enrol/paypal/edit.php
deleted file mode 100644 (file)
index 9c0541a..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_paypal to specified course
- * or edits current instance.
- *
- * @package    enrol_paypal
- * @copyright  2010 Petr Skoda  {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require('../../config.php');
-require_once('edit_form.php');
-
-$courseid   = required_param('courseid', PARAM_INT);
-$instanceid = optional_param('id', 0, PARAM_INT); // instanceid
-
-$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-$context = context_course::instance($course->id, MUST_EXIST);
-
-require_login($course);
-require_capability('enrol/paypal:config', $context);
-
-$PAGE->set_url('/enrol/paypal/edit.php', array('courseid'=>$course->id, 'id'=>$instanceid));
-$PAGE->set_pagelayout('admin');
-
-$return = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
-if (!enrol_is_enabled('paypal')) {
-    redirect($return);
-}
-
-$plugin = enrol_get_plugin('paypal');
-
-if ($instanceid) {
-    $instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'paypal', 'id'=>$instanceid), '*', MUST_EXIST);
-    $instance->cost = format_float($instance->cost, 2, true);
-} else {
-    require_capability('moodle/course:enrolconfig', $context);
-    // no instance yet, we have to add new instance
-    navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-    $instance = new stdClass();
-    $instance->id       = null;
-    $instance->courseid = $course->id;
-}
-
-$mform = new enrol_paypal_edit_form(NULL, array($instance, $plugin, $context));
-
-if ($mform->is_cancelled()) {
-    redirect($return);
-
-} else if ($data = $mform->get_data()) {
-    if ($instance->id) {
-        $reset = ($instance->status != $data->status);
-
-        $instance->status         = $data->status;
-        $instance->name           = $data->name;
-        $instance->cost           = unformat_float($data->cost);
-        $instance->currency       = $data->currency;
-        $instance->roleid         = $data->roleid;
-        $instance->enrolperiod    = $data->enrolperiod;
-        $instance->enrolstartdate = $data->enrolstartdate;
-        $instance->enrolenddate   = $data->enrolenddate;
-        $instance->timemodified   = time();
-        $DB->update_record('enrol', $instance);
-        \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
-
-        if ($reset) {
-            $context->mark_dirty();
-        }
-
-    } else {
-        $fields = array('status'=>$data->status, 'name'=>$data->name, 'cost'=>unformat_float($data->cost), 'currency'=>$data->currency, 'roleid'=>$data->roleid,
-                        'enrolperiod'=>$data->enrolperiod, 'enrolstartdate'=>$data->enrolstartdate, 'enrolenddate'=>$data->enrolenddate);
-        $plugin->add_instance($course, $fields);
-    }
-
-    redirect($return);
-}
-
-$PAGE->set_heading($course->fullname);
-$PAGE->set_title(get_string('pluginname', 'enrol_paypal'));
-
-echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('pluginname', 'enrol_paypal'));
-$mform->display();
-echo $OUTPUT->footer();
diff --git a/enrol/paypal/edit_form.php b/enrol/paypal/edit_form.php
deleted file mode 100644 (file)
index 8aa8059..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_paypal to specified course
- * or edits current instance.
- *
- * @package    enrol_paypal
- * @copyright  2010 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.'/formslib.php');
-
-class enrol_paypal_edit_form extends moodleform {
-
-    function definition() {
-        $mform = $this->_form;
-
-        list($instance, $plugin, $context) = $this->_customdata;
-
-        $mform->addElement('header', 'header', get_string('pluginname', 'enrol_paypal'));
-
-        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
-        $mform->setType('name', PARAM_TEXT);
-
-        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
-                         ENROL_INSTANCE_DISABLED => get_string('no'));
-        $mform->addElement('select', 'status', get_string('status', 'enrol_paypal'), $options);
-        $mform->setDefault('status', $plugin->get_config('status'));
-
-        $mform->addElement('text', 'cost', get_string('cost', 'enrol_paypal'), array('size'=>4));
-        $mform->setType('cost', PARAM_RAW); // Use unformat_float to get real value.
-        $mform->setDefault('cost', format_float($plugin->get_config('cost'), 2, true));
-
-        $paypalcurrencies = $plugin->get_currencies();
-        $mform->addElement('select', 'currency', get_string('currency', 'enrol_paypal'), $paypalcurrencies);
-        $mform->setDefault('currency', $plugin->get_config('currency'));
-
-        if ($instance->id) {
-            $roles = get_default_enrol_roles($context, $instance->roleid);
-        } else {
-            $roles = get_default_enrol_roles($context, $plugin->get_config('roleid'));
-        }
-        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_paypal'), $roles);
-        $mform->setDefault('roleid', $plugin->get_config('roleid'));
-
-
-        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_paypal'), array('optional' => true, 'defaultunit' => 86400));
-        $mform->setDefault('enrolperiod', $plugin->get_config('enrolperiod'));
-        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_paypal');
-
-        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_paypal'), array('optional' => true));
-        $mform->setDefault('enrolstartdate', 0);
-        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_paypal');
-
-        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_paypal'), array('optional' => true));
-        $mform->setDefault('enrolenddate', 0);
-        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_paypal');
-
-        $mform->addElement('hidden', 'id');
-        $mform->setType('id', PARAM_INT);
-
-        $mform->addElement('hidden', 'courseid');
-        $mform->setType('courseid', PARAM_INT);
-
-        if (enrol_accessing_via_instance($instance)) {
-            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), get_string('instanceeditselfwarningtext', 'core_enrol'));
-        }
-
-        $this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
-
-        $this->set_data($instance);
-    }
-
-    function validation($data, $files) {
-        global $DB, $CFG;
-        $errors = parent::validation($data, $files);
-
-        list($instance, $plugin, $context) = $this->_customdata;
-
-        if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
-            $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_paypal');
-        }
-
-        $cost = str_replace(get_string('decsep', 'langconfig'), '.', $data['cost']);
-        if (!is_numeric($cost)) {
-            $errors['cost'] = get_string('costerror', 'enrol_paypal');
-        }
-
-        return $errors;
-    }
-}
index 845e2e4..87ecfe5 100644 (file)
@@ -97,61 +97,54 @@ class enrol_paypal_plugin extends enrol_plugin {
     }
 
     /**
-     * Sets up navigation entries.
-     *
-     * @param object $instance
-     * @return void
+     * Returns true if the user can add a new instance in this course.
+     * @param int $courseid
+     * @return boolean
      */
-    public function add_course_navigation($instancesnode, stdClass $instance) {
-        if ($instance->enrol !== 'paypal') {
-             throw new coding_exception('Invalid enrol instance type!');
-        }
+    public function can_add_instance($courseid) {
+        $context = context_course::instance($courseid, MUST_EXIST);
 
-        $context = context_course::instance($instance->courseid);
-        if (has_capability('enrol/paypal:config', $context)) {
-            $managelink = new moodle_url('/enrol/paypal/edit.php', array('courseid'=>$instance->courseid, 'id'=>$instance->id));
-            $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
+        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/paypal:config', $context)) {
+            return false;
         }
+
+        // multiple instances supported - different cost for different roles
+        return true;
     }
 
     /**
-     * Returns edit icons for the page with list of instances
-     * @param stdClass $instance
-     * @return array
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
      */
-    public function get_action_icons(stdClass $instance) {
-        global $OUTPUT;
-
-        if ($instance->enrol !== 'paypal') {
-            throw new coding_exception('invalid enrol instance!');
-        }
-        $context = context_course::instance($instance->courseid);
-
-        $icons = array();
+    public function use_standard_editing_ui() {
+        return true;
+    }
 
-        if (has_capability('enrol/paypal:config', $context)) {
-            $editlink = new moodle_url("/enrol/paypal/edit.php", array('courseid'=>$instance->courseid, 'id'=>$instance->id));
-            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
-                    array('class' => 'iconsmall')));
+    /**
+     * Add new instance of enrol plugin.
+     * @param object $course
+     * @param array $fields instance fields
+     * @return int id of new instance, null if can not be created
+     */
+    public function add_instance($course, array $fields = null) {
+        if ($fields && !empty($fields['cost'])) {
+            $fields['cost'] = unformat_float($fields['cost']);
         }
-
-        return $icons;
+        return parent::add_instance($course, $fields);
     }
 
     /**
-     * Returns link to page which may be used to add new instance of enrolment plugin in course.
-     * @param int $courseid
-     * @return moodle_url page url
+     * Update instance of enrol plugin.
+     * @param stdClass $instance
+     * @param stdClass $data modified instance fields
+     * @return boolean
      */
-    public function get_newinstance_link($courseid) {
-        $context = context_course::instance($courseid, MUST_EXIST);
-
-        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/paypal:config', $context)) {
-            return NULL;
+    public function update_instance($instance, $data) {
+        if ($data) {
+            $data->cost = unformat_float($data->cost);
         }
-
-        // multiple instances supported - different cost for different roles
-        return new moodle_url('/enrol/paypal/edit.php', array('courseid'=>$courseid));
+        return parent::update_instance($instance, $data);
     }
 
     /**
@@ -312,6 +305,126 @@ class enrol_paypal_plugin extends enrol_plugin {
         $this->process_expirations($trace);
     }
 
+    /**
+     * Return an array of valid options for the status.
+     *
+     * @return array
+     */
+    protected function get_status_options() {
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the roleid.
+     *
+     * @param stdClass $instance
+     * @param context $context
+     * @return array
+     */
+    protected function get_roleid_options($instance, $context) {
+        if ($instance->id) {
+            $roles = get_default_enrol_roles($context, $instance->roleid);
+        } else {
+            $roles = get_default_enrol_roles($context, $this->get_config('roleid'));
+        }
+        return $roles;
+    }
+
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $context
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
+
+        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
+        $mform->setType('name', PARAM_TEXT);
+
+        $options = $this->get_status_options();
+        $mform->addElement('select', 'status', get_string('status', 'enrol_paypal'), $options);
+        $mform->setDefault('status', $this->get_config('status'));
+
+        $mform->addElement('text', 'cost', get_string('cost', 'enrol_paypal'), array('size' => 4));
+        $mform->setType('cost', PARAM_RAW);
+        $mform->setDefault('cost', format_float($this->get_config('cost'), 2, true));
+
+        $paypalcurrencies = $this->get_currencies();
+        $mform->addElement('select', 'currency', get_string('currency', 'enrol_paypal'), $paypalcurrencies);
+        $mform->setDefault('currency', $this->get_config('currency'));
+
+        $roles = $this->get_roleid_options($instance, $context);
+        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_paypal'), $roles);
+        $mform->setDefault('roleid', $this->get_config('roleid'));
+
+        $options = array('optional' => true, 'defaultunit' => 86400);
+        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_paypal'), $options);
+        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
+        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_paypal');
+
+        $options = array('optional' => true);
+        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_paypal'), $options);
+        $mform->setDefault('enrolstartdate', 0);
+        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_paypal');
+
+        $options = array('optional' => true);
+        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_paypal'), $options);
+        $mform->setDefault('enrolenddate', 0);
+        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_paypal');
+
+        if (enrol_accessing_via_instance($instance)) {
+            $warningtext = get_string('instanceeditselfwarningtext', 'core_enrol');
+            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warningtext);
+        }
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        $errors = array();
+
+        if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
+            $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_paypal');
+        }
+
+        $cost = str_replace(get_string('decsep', 'langconfig'), '.', $data['cost']);
+        if (!is_numeric($cost)) {
+            $errors['cost'] = get_string('costerror', 'enrol_paypal');
+        }
+
+        $validstatus = array_keys($this->get_status_options());
+        $validcurrency = array_keys($this->get_currencies());
+        $validroles = array_keys($this->get_roleid_options($instance, $context));
+        $tovalidate = array(
+            'name' => PARAM_TEXT,
+            'status' => $validstatus,
+            'currency' => $validcurrency,
+            'roleid' => $validroles,
+            'enrolperiod' => PARAM_INT,
+            'enrolstartdate' => PARAM_INT,
+            'enrolenddate' => PARAM_INT
+        );
+
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
+
     /**
      * Execute synchronisation.
      * @param progress_trace $trace
diff --git a/enrol/self/edit.php b/enrol/self/edit.php
deleted file mode 100644 (file)
index f429c39..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_self to specified course
- * or edits current instance.
- *
- * @package    enrol_self
- * @copyright  2010 Petr Skoda  {@link http://skodak.org}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require('../../config.php');
-require_once('edit_form.php');
-
-$courseid   = required_param('courseid', PARAM_INT);
-$instanceid = optional_param('id', 0, PARAM_INT);
-
-$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-$context = context_course::instance($course->id, MUST_EXIST);
-
-require_login($course);
-require_capability('enrol/self:config', $context);
-
-$PAGE->set_url('/enrol/self/edit.php', array('courseid'=>$course->id, 'id'=>$instanceid));
-$PAGE->set_pagelayout('admin');
-
-$return = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
-if (!enrol_is_enabled('self')) {
-    redirect($return);
-}
-
-/** @var enrol_self_plugin $plugin */
-$plugin = enrol_get_plugin('self');
-
-if ($instanceid) {
-    $instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'self', 'id'=>$instanceid), '*', MUST_EXIST);
-
-} else {
-    require_capability('moodle/course:enrolconfig', $context);
-    // No instance yet, we have to add new instance.
-    navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
-
-    $instance = (object)$plugin->get_instance_defaults();
-    $instance->id       = null;
-    $instance->courseid = $course->id;
-    $instance->status   = ENROL_INSTANCE_ENABLED; // Do not use default for automatically created instances here.
-}
-
-// Merge these two settings to one value for the single selection element.
-if ($instance->notifyall and $instance->expirynotify) {
-    $instance->expirynotify = 2;
-}
-unset($instance->notifyall);
-
-$mform = new enrol_self_edit_form(NULL, array($instance, $plugin, $context));
-
-if ($mform->is_cancelled()) {
-    redirect($return);
-
-} else if ($data = $mform->get_data()) {
-    if ($data->expirynotify == 2) {
-        $data->expirynotify = 1;
-        $data->notifyall = 1;
-    } else {
-        $data->notifyall = 0;
-    }
-    if (!$data->expirynotify) {
-        // Keep previous/default value of disabled expirythreshold option.
-        $data->expirythreshold = $instance->expirythreshold;
-    }
-    if (!isset($data->customint6)) {
-        // Add previous value of newenrols if disabled.
-        $data->customint6 = $instance->customint6;
-    }
-
-    if ($instance->id) {
-        $reset = ($instance->status != $data->status);
-
-        $instance->status         = $data->status;
-        $instance->name           = $data->name;
-        $instance->password       = $data->password;
-        $instance->customint1     = $data->customint1;
-        $instance->customint2     = $data->customint2;
-        $instance->customint3     = $data->customint3;
-        $instance->customint4     = $data->customint4;
-        $instance->customint5     = $data->customint5;
-        $instance->customint6     = $data->customint6;
-        $instance->customtext1    = $data->customtext1;
-        $instance->roleid         = $data->roleid;
-        $instance->enrolperiod    = $data->enrolperiod;
-        $instance->expirynotify   = $data->expirynotify;
-        $instance->notifyall      = $data->notifyall;
-        $instance->expirythreshold = $data->expirythreshold;
-        $instance->enrolstartdate = $data->enrolstartdate;
-        $instance->enrolenddate   = $data->enrolenddate;
-        $instance->timemodified   = time();
-        $DB->update_record('enrol', $instance);
-        \core\event\enrol_instance_updated::create_from_record($instance)->trigger();
-
-        if ($reset) {
-            $context->mark_dirty();
-        }
-
-    } else {
-        $fields = array(
-            'status'          => $data->status,
-            'name'            => $data->name,
-            'password'        => $data->password,
-            'customint1'      => $data->customint1,
-            'customint2'      => $data->customint2,
-            'customint3'      => $data->customint3,
-            'customint4'      => $data->customint4,
-            'customint5'      => $data->customint5,
-            'customint6'      => $data->customint6,
-            'customtext1'     => $data->customtext1,
-            'roleid'          => $data->roleid,
-            'enrolperiod'     => $data->enrolperiod,
-            'expirynotify'    => $data->expirynotify,
-            'notifyall'       => $data->notifyall,
-            'expirythreshold' => $data->expirythreshold,
-            'enrolstartdate'  => $data->enrolstartdate,
-            'enrolenddate'    => $data->enrolenddate);
-        $plugin->add_instance($course, $fields);
-    }
-
-    redirect($return);
-}
-
-$PAGE->set_heading($course->fullname);
-$PAGE->set_title(get_string('pluginname', 'enrol_self'));
-
-echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('pluginname', 'enrol_self'));
-$mform->display();
-echo $OUTPUT->footer();
diff --git a/enrol/self/edit_form.php b/enrol/self/edit_form.php
deleted file mode 100644 (file)
index af10050..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Adds new instance of enrol_self to specified course
- * or edits current instance.
- *
- * @package    enrol_self
- * @copyright  2010 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.'/formslib.php');
-require_once($CFG->dirroot.'/cohort/lib.php');
-
-class enrol_self_edit_form extends moodleform {
-
-    function definition() {
-        global $DB;
-
-        $mform = $this->_form;
-
-        list($instance, $plugin, $context) = $this->_customdata;
-
-        $mform->addElement('header', 'header', get_string('pluginname', 'enrol_self'));
-
-        $nameattribs = array('size' => '20', 'maxlength' => '255');
-        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'), $nameattribs);
-        $mform->setType('name', PARAM_TEXT);
-        $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'server');
-
-        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
-                         ENROL_INSTANCE_DISABLED => get_string('no'));
-        $mform->addElement('select', 'status', get_string('status', 'enrol_self'), $options);
-        $mform->addHelpButton('status', 'status', 'enrol_self');
-
-        $options = array(1 => get_string('yes'), 0 => get_string('no'));
-        $mform->addElement('select', 'customint6', get_string('newenrols', 'enrol_self'), $options);
-        $mform->addHelpButton('customint6', 'newenrols', 'enrol_self');
-        $mform->disabledIf('customint6', 'status', 'eq', ENROL_INSTANCE_DISABLED);
-
-        $passattribs = array('size' => '20', 'maxlength' => '50');
-        $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_self'), $passattribs);
-        $mform->addHelpButton('password', 'password', 'enrol_self');
-        if (empty($instance->id) and $plugin->get_config('requirepassword')) {
-            $mform->addRule('password', get_string('required'), 'required', null, 'client');
-        }
-        $mform->addRule('password', get_string('maximumchars', '', 50), 'maxlength', 50, 'server');
-
-        $options = array(1 => get_string('yes'),
-                         0 => get_string('no'));
-        $mform->addElement('select', 'customint1', get_string('groupkey', 'enrol_self'), $options);
-        $mform->addHelpButton('customint1', 'groupkey', 'enrol_self');
-
-        $roles = $this->extend_assignable_roles($context, $instance->roleid);
-        $mform->addElement('select', 'roleid', get_string('role', 'enrol_self'), $roles);
-
-        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_self'), array('optional' => true, 'defaultunit' => 86400));
-        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_self');
-
-        $options = array(0 => get_string('no'), 1 => get_string('expirynotifyenroller', 'core_enrol'), 2 => get_string('expirynotifyall', 'core_enrol'));
-        $mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options);
-        $mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol');
-
-        $mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), array('optional' => false, 'defaultunit' => 86400));
-        $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol');
-        $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0);
-
-        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_self'), array('optional' => true));
-        $mform->setDefault('enrolstartdate', 0);
-        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_self');
-
-        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_self'), array('optional' => true));
-        $mform->setDefault('enrolenddate', 0);
-        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_self');
-
-        $options = array(0 => get_string('never'),
-                 1800 * 3600 * 24 => get_string('numdays', '', 1800),
-                 1000 * 3600 * 24 => get_string('numdays', '', 1000),
-                 365 * 3600 * 24 => get_string('numdays', '', 365),
-                 180 * 3600 * 24 => get_string('numdays', '', 180),
-                 150 * 3600 * 24 => get_string('numdays', '', 150),
-                 120 * 3600 * 24 => get_string('numdays', '', 120),
-                 90 * 3600 * 24 => get_string('numdays', '', 90),
-                 60 * 3600 * 24 => get_string('numdays', '', 60),
-                 30 * 3600 * 24 => get_string('numdays', '', 30),
-                 21 * 3600 * 24 => get_string('numdays', '', 21),
-                 14 * 3600 * 24 => get_string('numdays', '', 14),
-                 7 * 3600 * 24 => get_string('numdays', '', 7));
-        $mform->addElement('select', 'customint2', get_string('longtimenosee', 'enrol_self'), $options);
-        $mform->addHelpButton('customint2', 'longtimenosee', 'enrol_self');
-
-        $mform->addElement('text', 'customint3', get_string('maxenrolled', 'enrol_self'));
-        $mform->addHelpButton('customint3', 'maxenrolled', 'enrol_self');
-        $mform->setType('customint3', PARAM_INT);
-
-        $cohorts = array(0 => get_string('no'));
-        $allcohorts = cohort_get_available_cohorts($context, 0, 0, 0);
-        if ($instance->customint5 && !isset($allcohorts[$instance->customint5]) &&
-                ($c = $DB->get_record('cohort', array('id' => $instance->customint5), 'id, name, idnumber, contextid, visible', IGNORE_MISSING))) {
-            // Current cohort was not found because current user can not see it. Still keep it.
-            $allcohorts[$instance->customint5] = $c;
-        }
-        foreach ($allcohorts as $c) {
-            $cohorts[$c->id] = format_string($c->name, true, array('context' => context::instance_by_id($c->contextid)));
-            if ($c->idnumber) {
-                $cohorts[$c->id] .= ' ['.s($c->idnumber).']';
-            }
-        }
-        if ($instance->customint5 && !isset($allcohorts[$instance->customint5])) {
-            // Somebody deleted a cohort, better keep the wrong value so that random ppl can not enrol.
-            $cohorts[$instance->customint5] = get_string('unknowncohort', 'cohort', $instance->customint5);
-        }
-        if (count($cohorts) > 1) {
-            $mform->addElement('select', 'customint5', get_string('cohortonly', 'enrol_self'), $cohorts);
-            $mform->addHelpButton('customint5', 'cohortonly', 'enrol_self');
-        } else {
-            $mform->addElement('hidden', 'customint5');
-            $mform->setType('customint5', PARAM_INT);
-            $mform->setConstant('customint5', 0);
-        }
-
-        $mform->addElement('advcheckbox', 'customint4', get_string('sendcoursewelcomemessage', 'enrol_self'));
-        $mform->addHelpButton('customint4', 'sendcoursewelcomemessage', 'enrol_self');
-
-        $mform->addElement('textarea', 'customtext1', get_string('customwelcomemessage', 'enrol_self'), array('cols'=>'60', 'rows'=>'8'));
-        $mform->addHelpButton('customtext1', 'customwelcomemessage', 'enrol_self');
-
-        $mform->addElement('hidden', 'id');
-        $mform->setType('id', PARAM_INT);
-        $mform->addElement('hidden', 'courseid');
-        $mform->setType('courseid', PARAM_INT);
-
-        if (enrol_accessing_via_instance($instance)) {
-            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), get_string('instanceeditselfwarningtext', 'core_enrol'));
-        }
-
-        $this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
-
-        $this->set_data($instance);
-    }
-
-    function validation($data, $files) {
-        global $DB, $CFG;
-        $errors = parent::validation($data, $files);
-
-        list($instance, $plugin, $context) = $this->_customdata;
-        $checkpassword = false;
-
-        if ($instance->id) {
-            // Check the password if we are enabling the plugin again.
-            if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
-                $checkpassword = true;
-            }
-
-            // Check the password if the instance is enabled and the password has changed.
-            if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
-                $checkpassword = true;
-            }
-        } else {
-            $checkpassword = true;
-        }
-
-        if ($checkpassword) {
-            $require = $plugin->get_config('requirepassword');
-            $policy  = $plugin->get_config('usepasswordpolicy');
-            if ($require and trim($data['password']) === '') {
-                $errors['password'] = get_string('required');
-            } else if (!empty($data['password']) && $policy) {
-                $errmsg = '';
-                if (!check_password_policy($data['password'], $errmsg)) {
-                    $errors['password'] = $errmsg;
-                }
-            }
-        }
-
-        if ($data['status'] == ENROL_INSTANCE_ENABLED) {
-            if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
-                $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_self');
-            }
-        }
-
-        if ($data['expirynotify'] > 0 and $data['expirythreshold'] < 86400) {
-            $errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol');
-        }
-
-        return $errors;
-    }
-
-    /**
-    * Gets a list of roles that this user can assign for the course as the default for self-enrolment.
-    *
-    * @param context $context the context.
-    * @param integer $defaultrole the id of the role that is set as the default for self-enrolment
-    * @return array index is the role id, value is the role name
-    */
-    function extend_assignable_roles($context, $defaultrole) {
-        global $DB;
-
-        $roles = get_assignable_roles($context, ROLENAME_BOTH);
-        if (!isset($roles[$defaultrole])) {
-            if ($role = $DB->get_record('role', array('id'=>$defaultrole))) {
-                $roles[$defaultrole] = role_get_name($role, $context, ROLENAME_BOTH);
-            }
-        }
-        return $roles;
-    }
-}
index fef3f6f..7b2827e 100644 (file)
@@ -118,61 +118,19 @@ class enrol_self_plugin extends enrol_plugin {
     }
 
     /**
-     * Sets up navigation entries.
+     * Return true if we can add a new instance to this course.
      *
-     * @param stdClass $instancesnode
-     * @param stdClass $instance
-     * @return void
-     */
-    public function add_course_navigation($instancesnode, stdClass $instance) {
-        if ($instance->enrol !== 'self') {
-             throw new coding_exception('Invalid enrol instance type!');
-        }
-
-        $context = context_course::instance($instance->courseid);
-        if (has_capability('enrol/self:config', $context)) {
-            $managelink = new moodle_url('/enrol/self/edit.php', array('courseid'=>$instance->courseid, 'id'=>$instance->id));
-            $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
-        }
-    }
-
-    /**
-     * Returns edit icons for the page with list of instances
-     * @param stdClass $instance
-     * @return array
-     */
-    public function get_action_icons(stdClass $instance) {
-        global $OUTPUT;
-
-        if ($instance->enrol !== 'self') {
-            throw new coding_exception('invalid enrol instance!');
-        }
-        $context = context_course::instance($instance->courseid);
-
-        $icons = array();
-
-        if (has_capability('enrol/self:config', $context)) {
-            $editlink = new moodle_url("/enrol/self/edit.php", array('courseid'=>$instance->courseid, 'id'=>$instance->id));
-            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
-                array('class' => 'iconsmall')));
-        }
-
-        return $icons;
-    }
-
-    /**
-     * Returns link to page which may be used to add new instance of enrolment plugin in course.
      * @param int $courseid
-     * @return moodle_url page url
+     * @return boolean
      */
-    public function get_newinstance_link($courseid) {
+    public function can_add_instance($courseid) {
         $context = context_course::instance($courseid, MUST_EXIST);
 
         if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/self:config', $context)) {
-            return NULL;
+            return false;
         }
-        // Multiple instances supported - different roles with different password.
-        return new moodle_url('/enrol/self/edit.php', array('courseid'=>$courseid));
+
+        return true;
     }
 
     /**
@@ -236,7 +194,7 @@ class enrol_self_plugin extends enrol_plugin {
 
         if (true === $enrolstatus) {
             // This user can self enrol using this instance.
-            $form = new enrol_self_enrol_form(NULL, $instance);
+            $form = new enrol_self_enrol_form(null, $instance);
             $instanceid = optional_param('instance', 0, PARAM_INT);
             if ($instance->id == $instanceid) {
                 if ($data = $form->get_data()) {
@@ -708,4 +666,357 @@ class enrol_self_plugin extends enrol_plugin {
 
         return true;
     }
+
+    /**
+     * Return an array of valid options for the status.
+     *
+     * @return array
+     */
+    protected function get_status_options() {
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the newenrols property.
+     *
+     * @return array
+     */
+    protected function get_newenrols_options() {
+        $options = array(1 => get_string('yes'), 0 => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the groupkey property.
+     *
+     * @return array
+     */
+    protected function get_groupkey_options() {
+        $options = array(1 => get_string('yes'), 0 => get_string('no'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the expirynotify property.
+     *
+     * @return array
+     */
+    protected function get_expirynotify_options() {
+        $options = array(0 => get_string('no'),
+                         1 => get_string('expirynotifyenroller', 'core_enrol'),
+                         2 => get_string('expirynotifyall', 'core_enrol'));
+        return $options;
+    }
+
+    /**
+     * Return an array of valid options for the longtimenosee property.
+     *
+     * @return array
+     */
+    protected function get_longtimenosee_options() {
+        $options = array(0 => get_string('never'),
+                         1800 * 3600 * 24 => get_string('numdays', '', 1800),
+                         1000 * 3600 * 24 => get_string('numdays', '', 1000),
+                         365 * 3600 * 24 => get_string('numdays', '', 365),
+                         180 * 3600 * 24 => get_string('numdays', '', 180),
+                         150 * 3600 * 24 => get_string('numdays', '', 150),
+                         120 * 3600 * 24 => get_string('numdays', '', 120),
+                         90 * 3600 * 24 => get_string('numdays', '', 90),
+                         60 * 3600 * 24 => get_string('numdays', '', 60),
+                         30 * 3600 * 24 => get_string('numdays', '', 30),
+                         21 * 3600 * 24 => get_string('numdays', '', 21),
+                         14 * 3600 * 24 => get_string('numdays', '', 14),
+                         7 * 3600 * 24 => get_string('numdays', '', 7));
+        return $options;
+    }
+
+    /**
+     * Add elements to the edit instance form.
+     *
+     * @param stdClass $instance
+     * @param MoodleQuickForm $mform
+     * @param context $context
+     * @return bool
+     */
+    public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
+        global $CFG;
+
+        // Merge these two settings to one value for the single selection element.
+        if ($instance->notifyall and $instance->expirynotify) {
+            $instance->expirynotify = 2;
+        }
+        unset($instance->notifyall);
+
+        $nameattribs = array('size' => '20', 'maxlength' => '255');
+        $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'), $nameattribs);
+        $mform->setType('name', PARAM_TEXT);
+        $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'server');
+
+        $options = $this->get_status_options();
+        $mform->addElement('select', 'status', get_string('status', 'enrol_self'), $options);
+        $mform->addHelpButton('status', 'status', 'enrol_self');
+
+        $options = $this->get_newenrols_options();
+        $mform->addElement('select', 'customint6', get_string('newenrols', 'enrol_self'), $options);
+        $mform->addHelpButton('customint6', 'newenrols', 'enrol_self');
+        $mform->disabledIf('customint6', 'status', 'eq', ENROL_INSTANCE_DISABLED);
+
+        $passattribs = array('size' => '20', 'maxlength' => '50');
+        $mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_self'), $passattribs);
+        $mform->addHelpButton('password', 'password', 'enrol_self');
+        if (empty($instance->id) and $this->get_config('requirepassword')) {
+            $mform->addRule('password', get_string('required'), 'required', null, 'client');
+        }
+        $mform->addRule('password', get_string('maximumchars', '', 50), 'maxlength', 50, 'server');
+
+        $options = $this->get_groupkey_options();
+        $mform->addElement('select', 'customint1', get_string('groupkey', 'enrol_self'), $options);
+        $mform->addHelpButton('customint1', 'groupkey', 'enrol_self');
+
+        $roles = $this->extend_assignable_roles($context, $instance->roleid);
+        $mform->addElement('select', 'roleid', get_string('role', 'enrol_self'), $roles);
+
+        $options = array('optional' => true, 'defaultunit' => 86400);
+        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_self'), $options);
+        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_self');
+
+        $options = $this->get_expirynotify_options();
+        $mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options);
+        $mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol');
+
+        $options = array('optional' => false, 'defaultunit' => 86400);
+        $mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), $options);
+        $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol');
+        $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0);
+
+        $options = array('optional' => true);
+        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_self'), $options);
+        $mform->setDefault('enrolstartdate', 0);
+        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_self');
+
+        $options = array('optional' => true);
+        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_self'), $options);
+        $mform->setDefault('enrolenddate', 0);
+        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_self');
+
+        $options = $this->get_longtimenosee_options();
+        $mform->addElement('select', 'customint2', get_string('longtimenosee', 'enrol_self'), $options);
+        $mform->addHelpButton('customint2', 'longtimenosee', 'enrol_self');
+
+        $mform->addElement('text', 'customint3', get_string('maxenrolled', 'enrol_self'));
+        $mform->addHelpButton('customint3', 'maxenrolled', 'enrol_self');
+        $mform->setType('customint3', PARAM_INT);
+
+        require_once($CFG->dirroot.'/cohort/lib.php');
+
+        $cohorts = array(0 => get_string('no'));
+        $allcohorts = cohort_get_available_cohorts($context, 0, 0, 0);
+        if ($instance->customint5 && !isset($allcohorts[$instance->customint5])) {
+            $c = $DB->get_record('cohort',
+                                 array('id' => $instance->customint5),
+                                 'id, name, idnumber, contextid, visible',
+                                 IGNORE_MISSING);
+            if ($c) {
+                // Current cohort was not found because current user can not see it. Still keep it.
+                $allcohorts[$instance->customint5] = $c;
+            }
+        }
+        foreach ($allcohorts as $c) {
+            $cohorts[$c->id] = format_string($c->name, true, array('context' => context::instance_by_id($c->contextid)));
+            if ($c->idnumber) {
+                $cohorts[$c->id] .= ' ['.s($c->idnumber).']';
+            }
+        }
+        if ($instance->customint5 && !isset($allcohorts[$instance->customint5])) {
+            // Somebody deleted a cohort, better keep the wrong value so that random ppl can not enrol.
+            $cohorts[$instance->customint5] = get_string('unknowncohort', 'cohort', $instance->customint5);
+        }
+        if (count($cohorts) > 1) {
+            $mform->addElement('select', 'customint5', get_string('cohortonly', 'enrol_self'), $cohorts);
+            $mform->addHelpButton('customint5', 'cohortonly', 'enrol_self');
+        } else {
+            $mform->addElement('hidden', 'customint5');
+            $mform->setType('customint5', PARAM_INT);
+            $mform->setConstant('customint5', 0);
+        }
+
+        $mform->addElement('advcheckbox', 'customint4', get_string('sendcoursewelcomemessage', 'enrol_self'));
+        $mform->addHelpButton('customint4', 'sendcoursewelcomemessage', 'enrol_self');
+
+        $options = array('cols' => '60', 'rows' => '8');
+        $mform->addElement('textarea', 'customtext1', get_string('customwelcomemessage', 'enrol_self'), $options);
+        $mform->addHelpButton('customtext1', 'customwelcomemessage', 'enrol_self');
+
+        if (enrol_accessing_via_instance($instance)) {
+            $warntext = get_string('instanceeditselfwarningtext', 'core_enrol');
+            $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warntext);
+        }
+    }
+
+    /**
+     * We are a good plugin and don't invent our own UI/validation code path.
+     *
+     * @return boolean
+     */
+    public function use_standard_editing_ui() {
+        return true;
+    }
+
+    /**
+     * Perform custom validation of the data used to edit the instance.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @param object $instance The instance loaded from the DB
+     * @param context $context The context of the instance we are editing
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *         or an empty array if everything is OK.
+     * @return void
+     */
+    public function edit_instance_validation($data, $files, $instance, $context) {
+        $errors = array();
+
+        $checkpassword = false;
+
+        if ($instance->id) {
+            // Check the password if we are enabling the plugin again.
+            if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
+                $checkpassword = true;
+            }
+
+            // Check the password if the instance is enabled and the password has changed.
+            if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
+                $checkpassword = true;
+            }
+        } else {
+            $checkpassword = true;
+        }
+
+        if ($checkpassword) {
+            $require = $this->get_config('requirepassword');
+            $policy  = $this->get_config('usepasswordpolicy');
+            if ($require and trim($data['password']) === '') {
+                $errors['password'] = get_string('required');
+            } else if (!empty($data['password']) && $policy) {
+                $errmsg = '';
+                if (!check_password_policy($data['password'], $errmsg)) {
+                    $errors['password'] = $errmsg;
+                }
+            }
+        }
+
+        if ($data['status'] == ENROL_INSTANCE_ENABLED) {
+            if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
+                $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_self');
+            }
+        }
+
+        if ($data['expirynotify'] > 0 and $data['expirythreshold'] < 86400) {
+            $errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol');
+        }
+
+        // Now these ones are checked by quickforms, but we may be called by the upload enrolments tool, or a webservive.
+        if (core_text::strlen($data['name']) > 255) {
+            $errors['name'] = get_string('err_maxlength', 'form', 255);
+        }
+        $validstatus = array_keys($this->get_status_options());
+        $validnewenrols = array_keys($this->get_newenrols_options());
+        if (core_text::strlen($data['password']) > 50) {
+            $errors['name'] = get_string('err_maxlength', 'form', 50);
+        }
+        $validgroupkey = array_keys($this->get_groupkey_options());
+        $context = context_course::instance($instance->courseid);
+        $validroles = array_keys($this->extend_assignable_roles($context, $instance->roleid));
+        $validexpirynotify = array_keys($this->get_expirynotify_options());
+        $validlongtimenosee = array_keys($this->get_longtimenosee_options());
+        $tovalidate = array(
+            'enrolstartdate' => PARAM_INT,
+            'enrolenddate' => PARAM_INT,
+            'name' => PARAM_TEXT,
+            'customint1' => $validgroupkey,
+            'customint2' => $validlongtimenosee,
+            'customint3' => PARAM_INT,
+            'customint4' => PARAM_BOOL,
+            'customint5' => PARAM_INT,
+            'customint6' => $validnewenrols,
+            'status' => $validstatus,
+            'enrolperiod' => PARAM_INT,
+            'expirynotify' => $validexpirynotify,
+            'roleid' => $validroles
+        );
+        if ($data['expirynotify'] != 0) {
+            $tovalidate['expirythreshold'] = PARAM_INT;
+        }
+        $typeerrors = $this->validate_param_types($data, $tovalidate);
+        $errors = array_merge($errors, $typeerrors);
+
+        return $errors;
+    }
+
+    /**
+     * Add new instance of enrol plugin.
+     * @param object $course
+     * @param array $fields instance fields
+     * @return int id of new instance, null if can not be created
+     */
+    public function add_instance($course, array $fields = null) {
+        // In the form we are representing 2 db columns with one field.
+        if (!empty($fields) && !empty($fields['expirynotify'])) {
+            if ($fields['expirynotify'] == 2) {
+                $fields['expirynotify'] = 1;
+                $fields['notifyall'] = 1;
+            } else {
+                $fields['notifyall'] = 0;
+            }
+        }
+
+        return parent::add_instance($course, $fields);
+    }
+
+    /**
+     * Update instance of enrol plugin.
+     * @param stdClass $instance
+     * @param stdClass $data modified instance fields
+     * @return boolean
+     */
+    public function update_instance($instance, $data) {
+        // In the form we are representing 2 db columns with one field.
+        if ($data->expirynotify == 2) {
+            $data->expirynotify = 1;
+            $data->notifyall = 1;
+        } else {
+            $data->notifyall = 0;
+        }
+        // Keep previous/default value of disabled expirythreshold option.
+        if (!$data->expirynotify) {
+            $data->expirythreshold = $instance->expirythreshold;
+        }
+        // Add previous value of newenrols if disabled.
+        if (!isset($data->customint6)) {
+            $data->customint6 = $instance->customint6;
+        }
+
+        return parent::update_instance($instance, $data);
+    }
+
+    /**
+     * Gets a list of roles that this user can assign for the course as the default for self-enrolment.
+     *
+     * @param context $context the context.
+     * @param integer $defaultrole the id of the role that is set as the default for self-enrolment
+     * @return array index is the role id, value is the role name
+     */
+    public function extend_assignable_roles($context, $defaultrole) {
+        global $DB;
+
+        $roles = get_assignable_roles($context, ROLENAME_BOTH);
+        if (!isset($roles[$defaultrole])) {
+            if ($role = $DB->get_record('role', array('id' => $defaultrole))) {
+                $roles[$defaultrole] = role_get_name($role, $context, ROLENAME_BOTH);
+            }
+        }
+        return $roles;
+    }
 }
index 38a41e1..1fb1729 100644 (file)
@@ -1,5 +1,10 @@
 This files describes API changes in /enrol/* - plugins,
 information provided here is intended especially for developers.
+=== 3.0 ===
+Enrolment plugins UI have been consolidated. Plugins can implement use_standard_editing_ui() function
+and add edit_instance_form() and edit_instance_validation() methods instead of providing their own edit.php and form. They can
+then rely on the default implementation of get_action_icons and get_course_navigation. In future
+this will mean they can be called by webservices/user upload tools because they can validate their data.
 
 === 3.1 ===
 * core_enrol_external::get_enrolled_users now supports two additional parameters for ordering: sortby and sortdirection.
index 672888a..49ab967 100644 (file)
@@ -30,8 +30,7 @@ require_once $CFG->dirroot.'/grade/edit/tree/lib.php';
 $courseid        = required_param('id', PARAM_INT);
 $action          = optional_param('action', 0, PARAM_ALPHA);
 $eid             = optional_param('eid', 0, PARAM_ALPHANUM);
-$category        = optional_param('category', null, PARAM_INT);
-$aggregationtype = optional_param('aggregationtype', null, PARAM_INT);
+$weightsadjusted = optional_param('weightsadjusted', 0, PARAM_INT);
 
 $url = new moodle_url('/grade/edit/tree/index.php', array('id' => $courseid));
 $PAGE->set_url($url);
@@ -53,33 +52,6 @@ $PAGE->requires->js('/grade/edit/tree/functions.js');
 $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'tree', 'courseid'=>$courseid));
 $returnurl = $gpr->get_return_url(null);
 
-// Change category aggregation if requested
-if (!is_null($category) && !is_null($aggregationtype) && confirm_sesskey()) {
-    if (!$grade_category = grade_category::fetch(array('id'=>$category, 'courseid'=>$courseid))) {
-        print_error('invalidcategoryid');
-    }
-
-    $data = new stdClass();
-    $data->aggregation = $aggregationtype;
-    grade_category::set_properties($grade_category, $data);
-    $grade_category->update();
-
-    grade_regrade_final_grades($courseid);
-}
-
-//first make sure we have proper final grades - we need it for locking changes
-$normalisationmessage = null;
-
-$originalweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
-
-grade_regrade_final_grades($courseid);
-
-$alteredweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
-
-if (array_diff($originalweights, $alteredweights)) {
-    $normalisationmessage = get_string('weightsadjusted', 'grades');
-}
-
 // get the grading tree object
 // note: total must be first for moving to work correctly, if you want it last moving code must be rewritten!
 $gtree = new grade_tree($courseid, false, false);
@@ -233,15 +205,32 @@ if ($data = data_submitted() and confirm_sesskey()) {
             $recreatetree = true;
         }
     }
+}
 
-    $originalweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
+$originalweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
 
-    grade_regrade_final_grades($courseid);
+/**
+ * Callback function to adjust the URL if weights changed after the
+ * regrade.
+ *
+ * @param int $courseid The course ID
+ * @param array $originalweights The weights before the regrade
+ * @param int $weightsadjusted Whether weights have been adjusted
+ * @return moodle_url A URL to redirect to after regrading when a progress bar is displayed.
+ */
+$grade_edit_tree_index_checkweights = function() use ($courseid, $originalweights, &$weightsadjusted) {
+    global $PAGE;
 
     $alteredweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
     if (array_diff($originalweights, $alteredweights)) {
-        $normalisationmessage = get_string('weightsadjusted', 'grades');
+        $weightsadjusted = 1;
+        return new moodle_url($PAGE->url, array('weightsadjusted' => $weightsadjusted));
     }
+    return $PAGE->url;
+};
+
+if (grade_regrade_final_grades_if_required($course, $grade_edit_tree_index_checkweights)) {
+    $recreatetree = true;
 }
 
 print_grade_page_head($courseid, 'settings', 'setup', get_string('gradebooksetup', 'grades'));
@@ -257,9 +246,10 @@ echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
 if ($recreatetree) {
     $grade_edit_tree = new grade_edit_tree($gtree, $movingeid, $gpr);
 }
+
 // Check to see if we have a normalisation message to send.
-if (!empty($normalisationmessage)) {
-    echo $OUTPUT->notification($normalisationmessage, 'notifymessage');
+if ($weightsadjusted) {
+    echo $OUTPUT->notification(get_string('weightsadjusted', 'grades'), 'notifymessage');
 }
 
 echo html_writer::table($grade_edit_tree->table);
index bf7b981..755fa8a 100644 (file)
@@ -699,8 +699,7 @@ class grade_export_update_buffer {
  * @param $courseid int The course being exported
  */
 function export_verify_grades($courseid) {
-    $regraderesult = grade_regrade_final_grades($courseid);
-    if (is_array($regraderesult)) {
-        throw new moodle_exception('gradecantregrade', 'error', '', implode(', ', array_unique($regraderesult)));
+    if (grade_needs_regrade_final_grades($courseid)) {
+        throw new moodle_exception('gradesneedregrading', 'grades');
     }
 }
index e044059..153a770 100644 (file)
@@ -214,9 +214,12 @@ class moodlequickform_guideeditor extends HTML_QuickForm_input {
                 if (!strlen(trim($criterion['maxscore']))) {
                     $errors['err_nomaxscore'] = 1;
                     $criterion['error_description'] = true;
-                } else if (!is_numeric($criterion['maxscore']) || $criterion['maxscore'] < 0) {
+                } else if (!is_numeric($criterion['maxscore'])) {
                     $errors['err_maxscorenotnumeric'] = 1;
                     $criterion['error_description'] = true;
+                } else if ($criterion['maxscore'] < 0) {
+                    $errors['err_maxscoreisnegative'] = 1;
+                    $criterion['error_description'] = true;
                 }
             }
             if (array_key_exists('moveup', $criterion) || $lastaction == 'movedown') {
index 87f7a11..71e2c5d 100644 (file)
@@ -50,6 +50,7 @@ $string['definemarkingguide'] = 'Define marking guide';
 $string['description'] = 'Description';
 $string['descriptionmarkers'] = 'Description for Markers';
 $string['descriptionstudents'] = 'Description for Students';
+$string['err_maxscoreisnegative'] = 'The max score is not valid, negative values are not allowed';
 $string['err_maxscorenotnumeric'] = 'Criterion max score must be numeric';
 $string['err_nocomment'] = 'Comment can not be empty';
 $string['err_nodescription'] = 'Student description can not be empty';
@@ -57,7 +58,8 @@ $string['err_nodescriptionmarkers'] = 'Marker description can not be empty';
 $string['err_nomaxscore'] = 'Criterion max score can not be empty';
 $string['err_noshortname'] = 'Criterion name can not be empty';
 $string['err_shortnametoolong'] = 'Criterion name must be less than 256 characters';
-$string['err_scoreinvalid'] = 'The score given to {$a->criterianame} is not valid, the max score is: {$a->maxscore}';
+$string['err_scoreinvalid'] = 'The score given to \'{$a->criterianame}\' is not valid, the max score is: {$a->maxscore}';
+$string['err_scoreisnegative'] = 'The score given to \'{$a->criterianame}\' is not valid, negative values are not allowed';
 $string['gradingof'] = '{$a} grading';
 $string['guide'] = 'Marking guide';
 $string['guidemappingexplained'] = 'WARNING: Your marking guide has a maximum grade of <b>{$a->maxscore} points</b> but the maximum grade set in your activity is {$a->modulegrade}  The maximum score set in your marking guide will be scaled to the maximum grade in the module.<br />
index a6c17eb..25c797e 100644 (file)
@@ -795,7 +795,7 @@ class gradingform_guide_instance extends gradingform_instance {
                     || $criterion['maxscore'] < $elementvalue['criteria'][$id]['score']
                     || !is_numeric($elementvalue['criteria'][$id]['score'])
                     || $elementvalue['criteria'][$id]['score'] < 0) {
-                $this->validationerrors[$id]['score'] =  $elementvalue['criteria'][$id]['score'];
+                $this->validationerrors[$id]['score'] = $elementvalue['criteria'][$id]['score'];
             }
         }
         if (!empty($this->validationerrors)) {
@@ -943,8 +943,13 @@ class gradingform_guide_instance extends gradingform_instance {
                     $a = new stdClass();
                     $a->criterianame = s($criteria[$id]['shortname']);
                     $a->maxscore = $criteria[$id]['maxscore'];
-                    $html .= html_writer::tag('div', get_string('err_scoreinvalid', 'gradingform_guide', $a),
+                    if ($this->validationerrors[$id]['score'] < 0) {
+                        $html .= html_writer::tag('div', get_string('err_scoreisnegative', 'gradingform_guide', $a),
                         array('class' => 'gradingform_guide-error'));
+                    } else {
+                        $html .= html_writer::tag('div', get_string('err_scoreinvalid', 'gradingform_guide', $a),
+                        array('class' => 'gradingform_guide-error'));
+                    }
                 }
             }
         }
index 6ea8a4b..72b6b60 100644 (file)
@@ -450,6 +450,8 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
                     break;
                 default:
                     if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN && $value) {
+                        // Id should be different then the actual input added later.
+                        $attrs['id'] .= '_hidden';
                         $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
                     }
                     // Display option as checkbox
@@ -461,6 +463,8 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
                     if ($mode == gradingform_rubric_controller::DISPLAY_EDIT_FROZEN || $mode == gradingform_rubric_controller::DISPLAY_PREVIEW) {
                         $attrs['disabled'] = 'disabled';
                         unset($attrs['name']);
+                        // Id should be different then the actual input added later.
+                        $attrs['id'] .= '_disabled';
                     }
                     $html .= html_writer::empty_tag('input', $attrs);
                     $html .= html_writer::tag('label', get_string($option, 'gradingform_rubric'), array('for' => $attrs['id']));
index 52c296d..66cf003 100644 (file)
@@ -1002,6 +1002,11 @@ function print_grade_page_head($courseid, $active_type, $active_plugin=null,
         grade_extend_settings($plugin_info, $courseid);
     }
 
+    // Set the current report as active in the breadcrumbs.
+    if ($active_plugin !== null && $reportnav = $PAGE->settingsnav->find($active_plugin, navigation_node::TYPE_SETTING)) {
+        $reportnav->make_active();
+    }
+
     $returnval = $OUTPUT->header();
 
     if (!$return) {
index f15efe2..7885fdc 100644 (file)
@@ -115,9 +115,6 @@ if (!is_null($toggle) && !empty($toggle_type)) {
     set_user_preferences(array('grade_report_show'.$toggle_type => $toggle));
 }
 
-//first make sure we have proper final grades - this must be done before constructing of the grade tree
-grade_regrade_final_grades($courseid);
-
 // Perform actions
 if (!empty($target) && !empty($action) && confirm_sesskey()) {
     grade_report_grader::do_process_action($target, $action, $courseid);
@@ -125,6 +122,9 @@ if (!empty($target) && !empty($action) && confirm_sesskey()) {
 
 $reportname = get_string('pluginname', 'gradereport_grader');
 
+// Do this check just before printing the grade header (and only do it once).
+grade_regrade_final_grades_if_required($course);
+
 // Print header
 print_grade_page_head($COURSE->id, 'report', 'grader', $reportname, false, $buttons);
 
index d0b13f5..53d7420 100644 (file)
@@ -1603,8 +1603,9 @@ class grade_report_grader extends grade_report {
         }
 
         $name = shorten_text($element['object']->get_name());
-        $courseheader = html_writer::tag('span', $name, array('id' => 'courseheader'));
-        $courseheader .= html_writer::label($showing, 'courseheader', false, array('class' => 'accesshide'));
+        $courseheaderid = 'courseheader_' . clean_param($name, PARAM_ALPHANUMEXT);
+        $courseheader = html_writer::tag('span', $name, array('id' => $courseheaderid));
+        $courseheader .= html_writer::label($showing, $courseheaderid, false, array('class' => 'accesshide'));
         $courseheader .= $icon;
 
         return $courseheader;
index eee8576..bb75576 100644 (file)
Binary files a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js and b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js differ
index 1ad586e..a151a1b 100644 (file)
Binary files a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js and b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-min.js differ
index 9636d4e..6fcd0dd 100644 (file)
Binary files a/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js and b/grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js differ
index e4e78f2..6d496bb 100644 (file)
@@ -932,9 +932,24 @@ FloatingHeaders.prototype = {
         var userCells = Y.all(SELECTORS.USERCELL);
         this.userColumnHeader.one('.cell').setStyle('width', userWidth);
         this.userColumn.all('.cell').each(function(cell, idx) {
+            var height = userCells.item(idx).getComputedStyle(HEIGHT);
+            // Nasty hack to account for Internet Explorer
+            if(Y.UA.ie !== 0) {
+                var node = userCells.item(idx);
+                var allHeight = node.getDOMNode ?
+                    node.getDOMNode().getBoundingClientRect().height :
+                    node.get('offsetHeight');
+                var marginHeight = parseInt(node.getComputedStyle('marginTop'),10) +
+                    parseInt(node.getComputedStyle('marginBottom'),10);
+                var paddingHeight = parseInt(node.getComputedStyle('paddingTop'),10) +
+                    parseInt(node.getComputedStyle('paddingBottom'),10);
+                var borderHeight = parseInt(node.getComputedStyle('borderTopWidth'),10) +
+                    parseInt(node.getComputedStyle('borderBottomWidth'),10);
+                height = allHeight - marginHeight - paddingHeight - borderHeight;
+            }
             cell.setStyles({
                 width: userWidth,
-                height: userCells.item(idx).getComputedStyle(HEIGHT)
+                height: height
             });
         }, this);
 
index cc809ab..092373a 100644 (file)
@@ -40,7 +40,7 @@ $context = context_course::instance($course->id);
 require_capability('gradereport/outcomes:view', $context);
 
 // First make sure we have proper final grades.
-grade_regrade_final_grades($courseid);
+grade_regrade_final_grades_if_required($course);
 
 // Grab all outcomes used in course.
 $report_info = array();
index e46885c..14eef55 100644 (file)
@@ -107,8 +107,8 @@ if (!isset($USER->grade_last_report)) {
 }
 $USER->grade_last_report[$course->id] = 'overview';
 
-//first make sure we have proper final grades - this must be done before constructing of the grade tree
-grade_regrade_final_grades($courseid);
+// First make sure we have proper final grades.
+grade_regrade_final_grades_if_required($course);
 
 if (has_capability('moodle/grade:viewall', $context) && $courseid != SITEID) {
     // Please note this would be extremely slow if we wanted to implement this properly for all teachers.
index be9461b..9d6ab82 100644 (file)
@@ -46,7 +46,16 @@ if (empty($itemid)) {
 }
 
 $courseparams = array('id' => $courseid);
-$PAGE->set_url(new moodle_url('/grade/report/singleview/index.php', $courseparams));
+$pageparams = array(
+        'id'        => $courseid,
+        'group'     => $groupid,
+        'userid'    => $userid,
+        'itemid'    => $itemid,
+        'item'      => $itemtype,
+        'page'      => $page,
+        'perpage'   => $perpage,
+    );
+$PAGE->set_url(new moodle_url('/grade/report/singleview/index.php', $pageparams));
 $PAGE->set_pagelayout('incourse');
 
 if (!$course = $DB->get_record('course', $courseparams)) {
@@ -78,9 +87,8 @@ if (!isset($USER->grade_last_report)) {
 }
 $USER->grade_last_report[$course->id] = 'singleview';
 
-// First make sure we have proper final grades -
-// this must be done before constructing of the grade tree.
-grade_regrade_final_grades($courseid);
+// First make sure we have proper final grades.
+grade_regrade_final_grades_if_required($course);
 
 $report = new gradereport_singleview($courseid, $gpr, $context, $itemtype, $itemid);
 
index a06a182..218a832 100644 (file)
@@ -29,7 +29,6 @@ Feature: We can bulk insert grades for students in a course
       | assign | C1 | a3 | Test assignment three | Submit something! |
       | assign | C1 | a4 | Test assignment four | Submit nothing!    |
 
-  @javascript
   Scenario: I can bulk insert grades and check their override flags for grade view.
     Given I log in as "teacher1"
     And I follow "Course 1"
@@ -44,7 +43,7 @@ Feature: We can bulk insert grades for students in a course
     And I follow "Single view for Test assignment one"
     Then the field "Grade for james (Student) 1" matches value "50.00"
     And the field "Override for james (Student) 1" matches value "0"
-    And I click on "Perform bulk insert" "checkbox"
+    And I set the field "Perform bulk insert" to "1"
     And I set the field "Insert value" to "1.0"
     And I press "Save"
     And I press "Continue"
@@ -56,8