Merge branch 'w13_MDL-38671_m25_adodb' of git://github.com/skodak/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 26 Mar 2013 15:07:12 +0000 (16:07 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 26 Mar 2013 15:07:12 +0000 (16:07 +0100)
529 files changed:
admin/environment.xml
admin/settings/server.php
blocks/course_list/block_course_list.php
cache/forms.php
cohort/edit_form.php
cohort/lib.php
course/category.php
course/completion_form.php
course/delete_category_form.php
course/dnduploadlib.php
course/edit_form.php
course/editcategory.php
course/editcategory_form.php
course/externallib.php
course/format/renderer.php
course/lib.php
course/manage.php
course/moodleform_mod.php
course/pending.php
course/recent.php
course/renderer.php
course/request_form.php
course/search.php
course/tests/courselib_test.php
course/yui/toolboxes/toolboxes.js
enrol/database/settings.php
enrol/ldap/settings.php
filter/glossary/yui/autolinker/autolinker.js [deleted file]
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-coverage.js [new file with mode: 0644]
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js [new file with mode: 0644]
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js [new file with mode: 0644]
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js [new file with mode: 0644]
filter/glossary/yui/src/autolinker/build.json [new file with mode: 0644]
filter/glossary/yui/src/autolinker/js/autolinker.js [new file with mode: 0644]
filter/glossary/yui/src/autolinker/meta/autolinker.json [new file with mode: 0644]
grade/externallib.php [new file with mode: 0644]
grade/grading/form/guide/lib.php
grade/grading/form/lib.php
grade/grading/form/rubric/lib.php
grade/tests/externallib_test.php [new file with mode: 0644]
group/group_form.php
group/lib.php
lang/en/admin.php
lang/en/cache.php
lang/en/error.php
lang/en/install.php
lang/en/moodle.php
lib/coursecatlib.php [new file with mode: 0644]
lib/datalib.php
lib/db/caches.php
lib/db/install.php
lib/db/services.php
lib/db/upgrade.php
lib/deprecatedlib.php
lib/filelib.php
lib/filestorage/tests/file_storage_test.php
lib/flowplayer/README.txt
lib/flowplayer/flowplayer-3.2.11.js [deleted file]
lib/flowplayer/flowplayer-3.2.11.min.js [deleted file]
lib/flowplayer/flowplayer-3.2.12.js [new file with mode: 0644]
lib/flowplayer/flowplayer-3.2.12.min.js [new file with mode: 0644]
lib/flowplayer/flowplayer-3.2.14.swf [deleted file]
lib/flowplayer/flowplayer-3.2.16.swf [new file with mode: 0644]
lib/flowplayer/flowplayer.controls-3.2.13.swf [deleted file]
lib/flowplayer/flowplayer.controls-3.2.15.swf [new file with mode: 0644]
lib/form/passwordunmask.php
lib/form/yui/passwordunmask/passwordunmask.js
lib/form/yui/shortforms/shortforms.js
lib/form/yui/showadvanced/showadvanced.js
lib/formslib.php
lib/gdlib.php
lib/htmlpurifier/CREDITS
lib/htmlpurifier/HTMLPurifier.php
lib/htmlpurifier/HTMLPurifier.safe-includes.php
lib/htmlpurifier/HTMLPurifier/AttrCollections.php
lib/htmlpurifier/HTMLPurifier/AttrDef.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Background.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Border.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Color.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Composite.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Filter.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Font.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Ident.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Length.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Multiple.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Number.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/Percentage.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php
lib/htmlpurifier/HTMLPurifier/AttrDef/CSS/URI.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Clone.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Enum.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Bool.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Class.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Color.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/ID.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Length.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php
lib/htmlpurifier/HTMLPurifier/AttrDef/HTML/Pixels.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Integer.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Lang.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Switch.php
lib/htmlpurifier/HTMLPurifier/AttrDef/Text.php
lib/htmlpurifier/HTMLPurifier/AttrDef/URI.php
lib/htmlpurifier/HTMLPurifier/AttrDef/URI/Email.php
lib/htmlpurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php
lib/htmlpurifier/HTMLPurifier/AttrDef/URI/Host.php
lib/htmlpurifier/HTMLPurifier/AttrDef/URI/IPv4.php
lib/htmlpurifier/HTMLPurifier/AttrDef/URI/IPv6.php
lib/htmlpurifier/HTMLPurifier/AttrTransform.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Background.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/BdoDir.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/BgColor.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/BoolToCSS.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Border.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/EnumToCSS.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/ImgRequired.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/ImgSpace.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Input.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Lang.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Length.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Name.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/NameSync.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Nofollow.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeEmbed.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeObject.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/SafeParam.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/ScriptRequired.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/TargetBlank.php
lib/htmlpurifier/HTMLPurifier/AttrTransform/Textarea.php
lib/htmlpurifier/HTMLPurifier/AttrTypes.php
lib/htmlpurifier/HTMLPurifier/AttrValidator.php
lib/htmlpurifier/HTMLPurifier/Bootstrap.php
lib/htmlpurifier/HTMLPurifier/CSSDefinition.php
lib/htmlpurifier/HTMLPurifier/ChildDef.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Chameleon.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Custom.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Empty.php
lib/htmlpurifier/HTMLPurifier/ChildDef/List.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Optional.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Required.php
lib/htmlpurifier/HTMLPurifier/ChildDef/StrictBlockquote.php
lib/htmlpurifier/HTMLPurifier/ChildDef/Table.php
lib/htmlpurifier/HTMLPurifier/Config.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Exception.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Interchange.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/Validator.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema.ser
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt
lib/htmlpurifier/HTMLPurifier/ConfigSchema/schema/info.ini
lib/htmlpurifier/HTMLPurifier/ContentSets.php
lib/htmlpurifier/HTMLPurifier/Context.php
lib/htmlpurifier/HTMLPurifier/Definition.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Decorator.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Null.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer.php
lib/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer/README [changed mode: 0644->0755]
lib/htmlpurifier/HTMLPurifier/DefinitionCacheFactory.php
lib/htmlpurifier/HTMLPurifier/Doctype.php
lib/htmlpurifier/HTMLPurifier/DoctypeRegistry.php
lib/htmlpurifier/HTMLPurifier/ElementDef.php
lib/htmlpurifier/HTMLPurifier/Encoder.php
lib/htmlpurifier/HTMLPurifier/EntityLookup.php
lib/htmlpurifier/HTMLPurifier/EntityParser.php
lib/htmlpurifier/HTMLPurifier/ErrorCollector.php
lib/htmlpurifier/HTMLPurifier/ErrorStruct.php
lib/htmlpurifier/HTMLPurifier/Exception.php
lib/htmlpurifier/HTMLPurifier/Filter.php
lib/htmlpurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php
lib/htmlpurifier/HTMLPurifier/Filter/YouTube.php
lib/htmlpurifier/HTMLPurifier/Generator.php
lib/htmlpurifier/HTMLPurifier/HTMLDefinition.php
lib/htmlpurifier/HTMLPurifier/HTMLModule.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Bdo.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/CommonAttributes.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Edit.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Forms.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Hypertext.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Iframe.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Image.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Legacy.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/List.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Name.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Nofollow.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Object.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Presentation.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Proprietary.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Ruby.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeEmbed.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeObject.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/SafeScripting.php [new file with mode: 0644]
lib/htmlpurifier/HTMLPurifier/HTMLModule/Scripting.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/StyleAttribute.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tables.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Target.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/TargetBlank.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Text.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy/Name.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php
lib/htmlpurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php
lib/htmlpurifier/HTMLPurifier/HTMLModuleManager.php
lib/htmlpurifier/HTMLPurifier/IDAccumulator.php
lib/htmlpurifier/HTMLPurifier/Injector.php
lib/htmlpurifier/HTMLPurifier/Injector/AutoParagraph.php
lib/htmlpurifier/HTMLPurifier/Injector/DisplayLinkURI.php
lib/htmlpurifier/HTMLPurifier/Injector/Linkify.php
lib/htmlpurifier/HTMLPurifier/Injector/PurifierLinkify.php
lib/htmlpurifier/HTMLPurifier/Injector/RemoveEmpty.php
lib/htmlpurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
lib/htmlpurifier/HTMLPurifier/Injector/SafeObject.php
lib/htmlpurifier/HTMLPurifier/Language.php
lib/htmlpurifier/HTMLPurifier/Language/classes/en-x-test.php
lib/htmlpurifier/HTMLPurifier/Language/messages/en-x-test.php
lib/htmlpurifier/HTMLPurifier/Language/messages/en-x-testmini.php
lib/htmlpurifier/HTMLPurifier/Language/messages/en.php
lib/htmlpurifier/HTMLPurifier/LanguageFactory.php
lib/htmlpurifier/HTMLPurifier/Length.php
lib/htmlpurifier/HTMLPurifier/Lexer.php
lib/htmlpurifier/HTMLPurifier/Lexer/DOMLex.php
lib/htmlpurifier/HTMLPurifier/Lexer/DirectLex.php
lib/htmlpurifier/HTMLPurifier/Lexer/PH5P.php
lib/htmlpurifier/HTMLPurifier/PercentEncoder.php
lib/htmlpurifier/HTMLPurifier/Printer.php
lib/htmlpurifier/HTMLPurifier/Printer/CSSDefinition.php
lib/htmlpurifier/HTMLPurifier/Printer/ConfigForm.css
lib/htmlpurifier/HTMLPurifier/Printer/ConfigForm.js
lib/htmlpurifier/HTMLPurifier/Printer/ConfigForm.php
lib/htmlpurifier/HTMLPurifier/Printer/HTMLDefinition.php
lib/htmlpurifier/HTMLPurifier/PropertyList.php
lib/htmlpurifier/HTMLPurifier/PropertyListIterator.php
lib/htmlpurifier/HTMLPurifier/Strategy.php
lib/htmlpurifier/HTMLPurifier/Strategy/Composite.php
lib/htmlpurifier/HTMLPurifier/Strategy/Core.php
lib/htmlpurifier/HTMLPurifier/Strategy/FixNesting.php
lib/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php
lib/htmlpurifier/HTMLPurifier/Strategy/RemoveForeignElements.php
lib/htmlpurifier/HTMLPurifier/Strategy/ValidateAttributes.php
lib/htmlpurifier/HTMLPurifier/StringHash.php
lib/htmlpurifier/HTMLPurifier/StringHashParser.php
lib/htmlpurifier/HTMLPurifier/TagTransform.php
lib/htmlpurifier/HTMLPurifier/TagTransform/Font.php
lib/htmlpurifier/HTMLPurifier/TagTransform/Simple.php
lib/htmlpurifier/HTMLPurifier/Token.php
lib/htmlpurifier/HTMLPurifier/Token/Comment.php
lib/htmlpurifier/HTMLPurifier/Token/Empty.php
lib/htmlpurifier/HTMLPurifier/Token/End.php
lib/htmlpurifier/HTMLPurifier/Token/Start.php
lib/htmlpurifier/HTMLPurifier/Token/Tag.php
lib/htmlpurifier/HTMLPurifier/Token/Text.php
lib/htmlpurifier/HTMLPurifier/TokenFactory.php
lib/htmlpurifier/HTMLPurifier/URI.php
lib/htmlpurifier/HTMLPurifier/URIDefinition.php
lib/htmlpurifier/HTMLPurifier/URIFilter.php
lib/htmlpurifier/HTMLPurifier/URIFilter/DisableExternal.php
lib/htmlpurifier/HTMLPurifier/URIFilter/DisableExternalResources.php
lib/htmlpurifier/HTMLPurifier/URIFilter/DisableResources.php
lib/htmlpurifier/HTMLPurifier/URIFilter/HostBlacklist.php
lib/htmlpurifier/HTMLPurifier/URIFilter/MakeAbsolute.php
lib/htmlpurifier/HTMLPurifier/URIFilter/Munge.php
lib/htmlpurifier/HTMLPurifier/URIFilter/SafeIframe.php
lib/htmlpurifier/HTMLPurifier/URIParser.php
lib/htmlpurifier/HTMLPurifier/URIScheme.php
lib/htmlpurifier/HTMLPurifier/URIScheme/data.php
lib/htmlpurifier/HTMLPurifier/URIScheme/file.php
lib/htmlpurifier/HTMLPurifier/URIScheme/ftp.php
lib/htmlpurifier/HTMLPurifier/URIScheme/http.php
lib/htmlpurifier/HTMLPurifier/URIScheme/https.php
lib/htmlpurifier/HTMLPurifier/URIScheme/mailto.php
lib/htmlpurifier/HTMLPurifier/URIScheme/news.php
lib/htmlpurifier/HTMLPurifier/URIScheme/nntp.php
lib/htmlpurifier/HTMLPurifier/URISchemeRegistry.php
lib/htmlpurifier/HTMLPurifier/UnitConverter.php
lib/htmlpurifier/HTMLPurifier/VarParser.php
lib/htmlpurifier/HTMLPurifier/VarParser/Flexible.php
lib/htmlpurifier/HTMLPurifier/VarParser/Native.php
lib/htmlpurifier/HTMLPurifier/VarParserException.php
lib/htmlpurifier/readme_moodle.txt
lib/javascript-static.js
lib/jquery/MIT-LICENSE.txt [new file with mode: 0644]
lib/jquery/jquery-1.9.1.js [new file with mode: 0644]
lib/jquery/jquery-1.9.1.min.js [new file with mode: 0644]
lib/jquery/jquery-migrate-1.1.1.js [new file with mode: 0644]
lib/jquery/jquery-migrate-1.1.1.min.js [new file with mode: 0644]
lib/jquery/plugins.php [new file with mode: 0644]
lib/jquery/readme_moodle.txt [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/animated-overlay.gif [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_flat_0_aaaaaa_40x100.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_flat_75_ffffff_40x100.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_glass_55_fbf9ee_1x400.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_glass_65_ffffff_1x400.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_glass_75_dadada_1x400.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_glass_75_e6e6e6_1x400.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_glass_95_fef1ec_1x400.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-icons_222222_256x240.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-icons_2e83ff_256x240.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-icons_454545_256x240.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-icons_888888_256x240.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/images/ui-icons_cd0a0a_256x240.png [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/jquery-ui.css [new file with mode: 0644]
lib/jquery/ui-1.10.2/css/base/jquery-ui.min.css [new file with mode: 0644]
lib/jquery/ui-1.10.2/jquery-ui.js [new file with mode: 0644]
lib/jquery/ui-1.10.2/jquery-ui.min.js [new file with mode: 0644]
lib/moodlelib.php
lib/outputlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/questionlib.php
lib/tcpdf/CHANGELOG.TXT
lib/tcpdf/README.TXT
lib/tcpdf/barcodes.php
lib/tcpdf/composer.json
lib/tcpdf/config/tcpdf_config.php
lib/tcpdf/datamatrix.php
lib/tcpdf/fonts/README.TXT [deleted file]
lib/tcpdf/fonts/arialunicid0.php [deleted file]
lib/tcpdf/pdf417.php
lib/tcpdf/readme_moodle.txt
lib/tcpdf/tcpdf.php
lib/testing/generator/block_generator.php
lib/testing/generator/component_generator_base.php [new file with mode: 0644]
lib/testing/generator/data_generator.php
lib/testing/generator/lib.php
lib/testing/generator/module_generator.php
lib/testing/tests/generator_test.php
lib/tests/blocklib_test.php
lib/tests/coursecatlib_test.php [new file with mode: 0644]
lib/tests/formslib_test.php
lib/tests/pagelib_test.php
lib/thirdpartylibs.xml
lib/timezone.txt
lib/upgrade.txt
mod/forum/tests/externallib_test.php
mod/forum/tests/generator/lib.php
mod/forum/tests/generator_test.php
mod/lesson/format.php
mod/lesson/lib.php
mod/lesson/locallib.php
mod/lesson/mod_form.php
mod/lesson/pagetypes/branchtable.php
mod/lesson/pagetypes/essay.php
mod/lesson/pagetypes/matching.php
mod/lesson/pagetypes/multichoice.php
mod/lesson/pagetypes/numerical.php
mod/lesson/pagetypes/shortanswer.php
mod/lesson/pagetypes/truefalse.php
mod/lesson/report.php
mod/lesson/view.php
mod/quiz/renderer.php
mod/scorm/locallib.php
mod/survey/lib.php
mod/url/mod_form.php
question/format/blackboard_six/format.php
question/format/blackboard_six/formatpool.php
question/format/blackboard_six/lang/en/qformat_blackboard_six.php
question/format/blackboard_six/tests/blackboardformatpool_test.php
question/tests/generator/lib.php [new file with mode: 0644]
question/tests/generator_test.php [new file with mode: 0644]
question/type/calculated/datasetdefinitions_form.php
question/type/calculated/datasetitems_form.php
report/courseoverview/index.php
report/log/locallib.php
report/stats/locallib.php
report/stats/user.php
theme/base/style/core.css
theme/base/style/user.css
theme/canvas/style/core.css
theme/jquery.php [new file with mode: 0644]
theme/magazine/style/core.css
theme/mymobile/config.php
theme/mymobile/javascript/jquery-1.7.1.min.js [deleted file]
theme/mymobile/jquery/custom.js [moved from theme/mymobile/javascript/custom.js with 100% similarity]
theme/mymobile/jquery/jquery.mobile-1.1.1.js [moved from theme/mymobile/javascript/jquery.mobile-1.1.1.js with 100% similarity]
theme/mymobile/jquery/plugins.php [new file with mode: 0644]
theme/mymobile/lib.php
theme/mymobile/renderers.php
theme/upgrade.txt
user/edit.php
user/edit_form.php
user/editadvanced.php
user/editadvanced_form.php
user/editlib.php
user/filters/courserole.php
user/profile.php
user/profile/lib.php
user/view.php
version.php

index 584a9b0..0afa5ec 100644 (file)
       </PHP_SETTING>
     </PHP_SETTINGS>
   </MOODLE>
+  <MOODLE version="2.5" requires="2.2">
+    <UNICODE level="required">
+      <FEEDBACK>
+        <ON_ERROR message="unicoderequired" />
+      </FEEDBACK>
+    </UNICODE>
+    <DATABASE level="required">
+      <VENDOR name="mysql" version="5.1.33" />
+      <VENDOR name="postgres" version="8.3" />
+      <VENDOR name="mssql" version="9.0" />
+      <VENDOR name="odbc_mssql" version="9.0" />
+      <VENDOR name="mssql_n" version="9.0" />
+      <VENDOR name="oracle" version="10.2" />
+      <VENDOR name="sqlite" version="2.0" />
+    </DATABASE>
+    <PHP version="5.3.3" level="required">
+    </PHP>
+    <PCREUNICODE level="optional">
+      <FEEDBACK>
+        <ON_CHECK message="pcreunicodewarning" />
+      </FEEDBACK>
+    </PCREUNICODE>
+    <PHP_EXTENSIONS>
+      <PHP_EXTENSION name="iconv" level="required">
+        <FEEDBACK>
+          <ON_CHECK message="iconvrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="mbstring" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="mbstringrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="curl" level="required">
+        <FEEDBACK>
+          <ON_CHECK message="curlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="openssl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opensslrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="tokenizer" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="tokenizerrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlrpc" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="xmlrpcrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="soap" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="soaprecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="ctype" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ctyperequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zip" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ziprequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="gd" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="gdrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="simplexml" level="required">
+        <FEEDBACK>
+          <ON_CHECK message="simplexmlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="spl" level="required">
+        <FEEDBACK>
+          <ON_CHECK message="splrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="pcre" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="dom" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xml" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="intl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="intlrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="json" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="hash" level="required"/>
+    </PHP_EXTENSIONS>
+    <PHP_SETTINGS>
+      <PHP_SETTING name="memory_limit" value="40M" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="settingmemorylimit" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="safe_mode" value="0" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingsafemode" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="file_uploads" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingfileuploads" />
+        </FEEDBACK>
+      </PHP_SETTING>
+    </PHP_SETTINGS>
+  </MOODLE>
 </COMPATIBILITY_MATRIX>
index 3ba7201..4a12d2a 100644 (file)
@@ -7,9 +7,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
 
 // "systempaths" settingpage
 $temp = new admin_settingpage('systempaths', new lang_string('systempaths','admin'));
-$temp->add(new admin_setting_configselect('gdversion', new lang_string('gdversion','admin'), new lang_string('configgdversion', 'admin'), check_gd_version(), array('0' => new lang_string('gdnot'),
-                                                                                                                                                          '1' => new lang_string('gd1'),
-                                                                                                                                                          '2' => new lang_string('gd2'))));
+
 $temp->add(new admin_setting_configexecutable('pathtodu', new lang_string('pathtodu', 'admin'), new lang_string('configpathtodu', 'admin'), ''));
 $temp->add(new admin_setting_configexecutable('aspellpath', new lang_string('aspellpath', 'admin'), new lang_string('edhelpaspellpath'), ''));
 $temp->add(new admin_setting_configexecutable('pathtodot', new lang_string('pathtodot', 'admin'), new lang_string('pathtodot_help', 'admin'), ''));
index 540c49a..a9aeb45 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 include_once($CFG->dirroot . '/course/lib.php');
+include_once($CFG->libdir . '/coursecatlib.php');
 
 class block_course_list extends block_list {
     function init() {
@@ -53,11 +54,11 @@ class block_course_list extends block_list {
             }
         }
 
-        $categories = get_categories("0");  // Parent = 0   ie top-level categories only
+        $categories = coursecat::get(0)->get_children();  // Parent = 0   ie top-level categories only
         if ($categories) {   //Check we have categories
             if (count($categories) > 1 || (count($categories) == 1 && $DB->count_records('course') > 200)) {     // Just print top level category links
                 foreach ($categories as $category) {
-                    $categoryname = format_string($category->name, true, array('context' => context_coursecat::instance($category->id)));
+                    $categoryname = $category->get_formatted_name();
                     $linkcss = $category->visible ? "" : " class=\"dimmed\" ";
                     $this->content->items[]="<a $linkcss href=\"$CFG->wwwroot/course/category.php?id=$category->id\">".$icon . $categoryname . "</a>";
                 }
index 542e7a0..9708a01 100644 (file)
@@ -49,7 +49,9 @@ class cachestore_addinstance_form extends moodleform {
         $locks = $this->_customdata['locks'];
 
         $form->addElement('hidden', 'plugin', $plugin);
+        $form->setType('plugin', PARAM_PLUGIN);
         $form->addElement('hidden', 'editing', !empty($this->_customdata['store']));
+        $form->setType('editing', PARAM_BOOL);
 
         if (!$store) {
             $form->addElement('text', 'name', get_string('storename', 'cache'));
@@ -59,6 +61,7 @@ class cachestore_addinstance_form extends moodleform {
         } else {
             $form->addElement('hidden', 'name', $store);
             $form->addElement('static', 'name-value', get_string('storename', 'cache'), $store);
+            $form->setType('name', PARAM_TEXT);
         }
 
         if (is_array($locks)) {
@@ -214,4 +217,4 @@ class cache_mode_mappings_form extends moodleform {
 
         $this->add_action_buttons();
     }
-}
\ No newline at end of file
+}
index c23c249..fb7409e 100644 (file)
@@ -85,9 +85,9 @@ class cohort_edit_form extends moodleform {
     }
 
     protected function get_category_options($currentcontextid) {
-        $displaylist = array();
-        $parentlist = array();
-        make_categories_list($displaylist, $parentlist, 'moodle/cohort:manage');
+        global $CFG;
+        require_once($CFG->libdir. '/coursecatlib.php');
+        $displaylist = coursecat::make_categories_list('moodle/cohort:manage');
         $options = array();
         $syscontext = context_system::instance();
         if (has_capability('moodle/cohort:manage', $syscontext)) {
index b71b00b..19441a5 100644 (file)
@@ -102,7 +102,7 @@ function cohort_delete_cohort($cohort) {
  * Somehow deal with cohorts when deleting course category,
  * we can not just delete them because they might be used in enrol
  * plugins or referenced in external systems.
- * @param  stdClass $category
+ * @param  stdClass|coursecat $category
  * @return void
  */
 function cohort_delete_category($category) {
index e32eba9..6ed1898 100644 (file)
@@ -26,6 +26,7 @@
 require_once("../config.php");
 require_once($CFG->dirroot.'/course/lib.php');
 require_once($CFG->libdir.'/textlib.class.php');
+require_once($CFG->libdir. '/coursecatlib.php');
 
 $id = required_param('id', PARAM_INT); // Category id
 $page = optional_param('page', 0, PARAM_INT); // which page to show
@@ -69,9 +70,7 @@ $PAGE->set_button(print_course_search('', true, 'navbar'));
 echo $OUTPUT->header();
 
 /// Print the category selector
-$displaylist = array();
-$notused = array();
-make_categories_list($displaylist, $notused);
+$displaylist = coursecat::make_categories_list();
 
 echo '<div class="categorypicker">';
 $select = new single_select(new moodle_url('/course/category.php'), 'id', $displaylist, $category->id, null, 'switchcategory');
index 4344aa3..7dab70a 100644 (file)
@@ -97,9 +97,8 @@ class course_completion_form extends moodleform {
             }
 
             // Get category list
-            $list = array();
-            $parents = array();
-            make_categories_list($list, $parents);
+            require_once($CFG->libdir. '/coursecatlib.php');
+            $list = coursecat::make_categories_list();
 
             // Get course list for select box
             $selectbox = array();
index e7af4b1..f76655e 100644 (file)
@@ -6,69 +6,25 @@ if (!defined('MOODLE_INTERNAL')) {
 
 require_once($CFG->libdir.'/formslib.php');
 require_once($CFG->libdir.'/questionlib.php');
+require_once($CFG->libdir. '/coursecatlib.php');
 
 class delete_category_form extends moodleform {
 
     var $_category;
 
     function definition() {
-        global $CFG, $DB;
-
-        $mform    =& $this->_form;
-        $category = $this->_customdata;
-        $categorycontext = context_coursecat::instance($category->id);
-        $this->_category = $category;
-
-    /// Check permissions, to see if it OK to give the option to delete
-    /// the contents, rather than move elsewhere.
-    /// Are there any subcategories of this one, can they be deleted?
-        $candeletecontent = true;
-        $tocheck = get_child_categories($category->id);
-        $containscategories = !empty($tocheck);
-        $categoryids = array($category->id);
-        while (!empty($tocheck)) {
-            $checkcat = array_pop($tocheck);
-            $childcategoryids[] = $checkcat->id;
-            $tocheck = $tocheck + get_child_categories($checkcat->id);
-            $chcontext = context_coursecat::instance($checkcat->id);
-            if ($candeletecontent && !has_capability('moodle/category:manage', $chcontext)) {
-                $candeletecontent = false;
-            }
-        }
-
-    /// Are there any courses in here, can they be deleted?
-        list($test, $params) = $DB->get_in_or_equal($categoryids);
-        $containedcourses = $DB->get_records_sql(
-                "SELECT id,1 FROM {course} c WHERE c.category $test", $params);
-        $containscourses = false;
-        if ($containedcourses) {
-            $containscourses = true;
-            foreach ($containedcourses as $courseid => $notused) {
-                if ($candeletecontent && !can_delete_course($courseid)) {
-                    $candeletecontent = false;
-                    break;
-                }
-            }
-        }
+        $mform = & $this->_form;
+        $this->_category = $this->_customdata;
+        $categorycontext = context_coursecat::instance($this->_category->id);
 
-    /// Are there any questions in the question bank here?
-        $containsquestions = question_context_has_any_questions($categorycontext);
+        // Check permissions, to see if it OK to give the option to delete
+        // the contents, rather than move elsewhere.
+        $candeletecontent = $this->_category->can_delete_full();
 
-    /// Get the list of categories we might be able to move to.
-        $testcaps = array();
-        if ($containscourses) {
-            $testcaps[] = 'moodle/course:create';
-        }
-        if ($containscategories || $containsquestions) {
-            $testcaps[] = 'moodle/category:manage';
-        }
-        $displaylist = array();
-        $notused = array();
-        if (!empty($testcaps)) {
-            make_categories_list($displaylist, $notused, $testcaps, $category->id);
-        }
+        // Get the list of categories we might be able to move to.
+        $displaylist = $this->_category->move_content_targets_list();
 
-    /// Now build the options.
+        // Now build the options.
         $options = array();
         if ($displaylist) {
             $options[0] = get_string('movecontentstoanothercategory');
@@ -76,60 +32,57 @@ class delete_category_form extends moodleform {
         if ($candeletecontent) {
             $options[1] = get_string('deleteallcannotundo');
         }
+        if (empty($options)) {
+            print_error('youcannotdeletecategory', 'error', 'index.php', $this->_category->get_formatted_name());
+        }
 
-    /// Now build the form.
-        $mform->addElement('header','general', get_string('categorycurrentcontents', '', format_string($category->name, true, array('context' => $categorycontext))));
+        // Now build the form.
+        $mform->addElement('header','general', get_string('categorycurrentcontents', '', $this->_category->get_formatted_name()));
 
-        if ($containscourses || $containscategories || $containsquestions) {
-            if (empty($options)) {
-                print_error('youcannotdeletecategory', 'error', 'index.php', format_string($category->name, true, array('context' => $categorycontext)));
-            }
+        // Describe the contents of this category.
+        $contents = '';
+        if ($this->_category->has_children()) {
+            $contents .= '<li>' . get_string('subcategories') . '</li>';
+        }
+        if ($this->_category->has_courses()) {
+            $contents .= '<li>' . get_string('courses') . '</li>';
+        }
+        if (question_context_has_any_questions($categorycontext)) {
+            $contents .= '<li>' . get_string('questionsinthequestionbank') . '</li>';
+        }
+        if (!empty($contents)) {
+            $mform->addElement('static', 'emptymessage', get_string('thiscategorycontains'), html_writer::tag('ul', $contents));
+        } else {
+            $mform->addElement('static', 'emptymessage', '', get_string('deletecategoryempty'));
+        }
 
-        /// Describe the contents of this category.
-            $contents = '<ul>';
-            if ($containscategories) {
-                $contents .= '<li>' . get_string('subcategories') . '</li>';
-            }
-            if ($containscourses) {
-                $contents .= '<li>' . get_string('courses') . '</li>';
-            }
-            if ($containsquestions) {
-                $contents .= '<li>' . get_string('questionsinthequestionbank') . '</li>';
-            }
-            $contents .= '</ul>';
-            $mform->addElement('static', 'emptymessage', get_string('thiscategorycontains'), $contents);
-
-        /// Give the options for what to do.
-            $mform->addElement('select', 'fulldelete', get_string('whattodo'), $options);
-            if (count($options) == 1) {
-                $optionkeys = array_keys($options);
-                $option = reset($optionkeys);
-                $mform->hardFreeze('fulldelete');
-                $mform->setConstant('fulldelete', $option);
-            }
+        // Give the options for what to do.
+        $mform->addElement('select', 'fulldelete', get_string('whattodo'), $options);
+        if (count($options) == 1) {
+            $optionkeys = array_keys($options);
+            $option = reset($optionkeys);
+            $mform->hardFreeze('fulldelete');
+            $mform->setConstant('fulldelete', $option);
+        }
 
-            if ($displaylist) {
-                $mform->addElement('select', 'newparent', get_string('movecategorycontentto'), $displaylist);
-                if (in_array($category->parent, $displaylist)) {
-                    $mform->setDefault('newparent', $category->parent);
-                }
-                $mform->disabledIf('newparent', 'fulldelete', 'eq', '1');
+        if ($displaylist) {
+            $mform->addElement('select', 'newparent', get_string('movecategorycontentto'), $displaylist);
+            if (in_array($this->_category->parent, $displaylist)) {
+                $mform->setDefault('newparent', $this->_category->parent);
             }
-        } else {
-            $mform->addElement('hidden', 'fulldelete', 1);
-            $mform->setType('fulldelete', PARAM_INT);
-            $mform->addElement('static', 'emptymessage', '', get_string('deletecategoryempty'));
+            $mform->disabledIf('newparent', 'fulldelete', 'eq', '1');
         }
 
         $mform->addElement('hidden', 'deletecat');
         $mform->setType('deletecat', PARAM_ALPHANUM);
         $mform->addElement('hidden', 'sure');
         $mform->setType('sure', PARAM_ALPHANUM);
-        $mform->setDefault('sure', md5(serialize($category)));
+        $mform->setDefault('sure', md5(serialize($this->_category)));
 
 //--------------------------------------------------------------------------------
         $this->add_action_buttons(true, get_string('delete'));
 
+        $this->set_data(array('deletecat' => $this->_category->id));
     }
 
 /// perform some extra moodle validation
index b7e8994..41076c9 100644 (file)
@@ -51,7 +51,7 @@ function dndupload_add_to_course($course, $modnames) {
     // Add the javascript to the page.
     $jsmodule = array(
         'name' => 'coursedndupload',
-        'fullpath' => new moodle_url('/course/dndupload.js'),
+        'fullpath' => '/course/dndupload.js',
         'strings' => array(
             array('addfilehere', 'moodle'),
             array('dndworkingfiletextlink', 'moodle'),
index ce7d8b7..bfa6ca6 100644 (file)
@@ -4,6 +4,7 @@ defined('MOODLE_INTERNAL') || die;
 
 require_once($CFG->libdir.'/formslib.php');
 require_once($CFG->libdir.'/completionlib.php');
+require_once($CFG->libdir. '/coursecatlib.php');
 
 class course_edit_form extends moodleform {
     protected $course;
@@ -48,9 +49,7 @@ class course_edit_form extends moodleform {
         // verify permissions to change course category or keep current
         if (empty($course->id)) {
             if (has_capability('moodle/course:create', $categorycontext)) {
-                $displaylist = array();
-                $parentlist = array();
-                make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+                $displaylist = coursecat::make_categories_list('moodle/course:create');
                 $mform->addElement('select', 'category', get_string('category'), $displaylist);
                 $mform->addHelpButton('category', 'category');
                 $mform->setDefault('category', $category->id);
@@ -61,12 +60,10 @@ class course_edit_form extends moodleform {
             }
         } else {
             if (has_capability('moodle/course:changecategory', $coursecontext)) {
-                $displaylist = array();
-                $parentlist = array();
-                make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+                $displaylist = coursecat::make_categories_list('moodle/course:create');
                 if (!isset($displaylist[$course->category])) {
                     //always keep current
-                    $displaylist[$course->category] = format_string($DB->get_field('course_categories', 'name', array('id'=>$course->category)));
+                    $displaylist[$course->category] = coursecat::get($course->category)->get_formatted_name();
                 }
                 $mform->addElement('select', 'category', get_string('category'), $displaylist);
                 $mform->addHelpButton('category', 'category');
@@ -190,7 +187,9 @@ class course_edit_form extends moodleform {
         }
 
 //--------------------------------------------------------------------------------
+        // Just a placeholder..
         $mform->addElement('hidden', 'addcourseformatoptionshere');
+        $mform->setType('addcourseformatoptionshere', PARAM_BOOL);
 
 //--------------------------------------------------------------------------------
         enrol_course_edit_form($mform, $course, $context);
index b7184b7..355aca1 100644 (file)
@@ -27,8 +27,9 @@
  */
 
 require_once('../config.php');
-require_once('lib.php');
-require_once('editcategory_form.php');
+require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->dirroot.'/course/editcategory_form.php');
+require_once($CFG->libdir.'/coursecatlib.php');
 
 require_login();
 
@@ -92,42 +93,16 @@ if ($mform->is_cancelled()) {
         redirect($CFG->wwwroot .'/course/manage.php');
     }
 } else if ($data = $mform->get_data()) {
-    $newcategory = new stdClass();
-    $newcategory->name = $data->name;
-    $newcategory->idnumber = $data->idnumber;
-    $newcategory->description_editor = $data->description_editor;
-    $newcategory->parent = $data->parent; // if $data->parent = 0, the new category will be a top-level category
-
-    if (isset($data->theme) && !empty($CFG->allowcategorythemes)) {
-        $newcategory->theme = $data->theme;
-    }
-
-    $logaction = 'update';
     if ($id) {
-        // Update an existing category.
-        $newcategory->id = $category->id;
-        if ($newcategory->parent != $category->parent) {
-            // check category manage capability if parent changed
-            require_capability('moodle/category:manage', get_category_or_system_context((int)$newcategory->parent));
-            $parent_cat = $DB->get_record('course_categories', array('id' => $newcategory->parent));
-            move_category($newcategory, $parent_cat);
+        $newcategory = coursecat::get($id);
+        if ($data->parent != $category->parent && !$newcategory->can_change_parent($data->parent)) {
+            print_error('cannotmovecategory');
         }
+        $newcategory->update($data, $editoroptions);
     } else {
-        // Create a new category.
-        $newcategory->description = $data->description_editor['text'];
-
-        // Don't overwrite the $newcategory object as it'll be processed by file_postupdate_standard_editor in a moment
-        $category = create_course_category($newcategory);
-        $newcategory->id = $category->id;
-        $categorycontext = $category->context;
-        $logaction = 'add';
+        $newcategory = coursecat::create($data, $editoroptions);
     }
 
-    $newcategory = file_postupdate_standard_editor($newcategory, 'description', $editoroptions, $categorycontext, 'coursecat', 'description', 0);
-    $DB->update_record('course_categories', $newcategory);
-    add_to_log(SITEID, "category", $logaction, "editcategory.php?id=$newcategory->id", $newcategory->id);
-    fix_course_sortorder();
-
     redirect('manage.php?id='.$newcategory->id);
 }
 
index 3e3cfe3..40ac48c 100644 (file)
@@ -4,6 +4,7 @@ if (!defined('MOODLE_INTERNAL')) {
 }
 
 require_once ($CFG->dirroot.'/course/moodleform_mod.php');
+require_once ($CFG->libdir.'/coursecatlib.php');
 class editcategory_form extends moodleform {
 
     // form definition
@@ -18,17 +19,16 @@ class editcategory_form extends moodleform {
         if (has_capability('moodle/category:manage', get_system_context()) || $category->parent == 0) {
             $options[0] = get_string('top');
         }
-        $parents = array();
         if ($category->id) {
             // Editing an existing category.
-            make_categories_list($options, $parents, 'moodle/category:manage', $category->id);
+            $options += coursecat::make_categories_list('moodle/category:manage', $category->id);
             if (empty($options[$category->parent])) {
                 $options[$category->parent] = $DB->get_field('course_categories', 'name', array('id'=>$category->parent));
             }
             $strsubmit = get_string('savechanges');
         } else {
             // Making a new category
-            make_categories_list($options, $parents, 'moodle/category:manage');
+            $options += coursecat::make_categories_list('moodle/category:manage');
             $strsubmit = get_string('createcategory');
         }
 
index 3063966..362f928 100644 (file)
@@ -1650,7 +1650,7 @@ class core_course_external extends external_api {
      */
     public static function create_categories($categories) {
         global $CFG, $DB;
-        require_once($CFG->dirroot . "/course/lib.php");
+        require_once($CFG->libdir . "/coursecatlib.php");
 
         $params = self::validate_parameters(self::create_categories_parameters(),
                         array('categories' => $categories));
@@ -1670,38 +1670,10 @@ class core_course_external extends external_api {
             self::validate_context($context);
             require_capability('moodle/category:manage', $context);
 
-            // Check name.
-            if (textlib::strlen($category['name'])>255) {
-                throw new moodle_exception('categorytoolong');
-            }
-
-            $newcategory = new stdClass();
-            $newcategory->name = $category['name'];
-            $newcategory->parent = $category['parent'];
-            // Format the description.
-            if (!empty($category['description'])) {
-                $newcategory->description = $category['description'];
-            }
-            $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
-            if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
-                $newcategory->theme = $category['theme'];
-            }
-            // Check id number.
-            if (!empty($category['idnumber'])) { // Same as in course/editcategory_form.php .
-                if (textlib::strlen($category['idnumber'])>100) {
-                    throw new moodle_exception('idnumbertoolong');
-                }
-                if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
-                    if ($existing->id) {
-                        throw new moodle_exception('idnumbertaken');
-                    }
-                }
-                $newcategory->idnumber = $category['idnumber'];
-            }
+            // this will validate format and throw an exception if there are errors
+            external_validate_format($category['descriptionformat']);
 
-            $newcategory = create_course_category($newcategory);
-            // Populate special fields.
-            fix_course_sortorder();
+            $newcategory = coursecat::create($category);
 
             $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
         }
@@ -1764,7 +1736,7 @@ class core_course_external extends external_api {
      */
     public static function update_categories($categories) {
         global $CFG, $DB;
-        require_once($CFG->dirroot . "/course/lib.php");
+        require_once($CFG->libdir . "/coursecatlib.php");
 
         // Validate parameters.
         $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
@@ -1772,49 +1744,16 @@ class core_course_external extends external_api {
         $transaction = $DB->start_delegated_transaction();
 
         foreach ($params['categories'] as $cat) {
-            if (!$category = $DB->get_record('course_categories', array('id' => $cat['id']))) {
-                throw new moodle_exception('unknowcategory');
-            }
+            $category = coursecat::get($cat['id']);
 
             $categorycontext = context_coursecat::instance($cat['id']);
             self::validate_context($categorycontext);
             require_capability('moodle/category:manage', $categorycontext);
 
-            if (!empty($cat['name'])) {
-                if (textlib::strlen($cat['name'])>255) {
-                     throw new moodle_exception('categorytoolong');
-                }
-                $category->name = $cat['name'];
-            }
-            if (!empty($cat['idnumber'])) {
-                if (textlib::strlen($cat['idnumber'])>100) {
-                    throw new moodle_exception('idnumbertoolong');
-                }
-                $category->idnumber = $cat['idnumber'];
-            }
-            if (!empty($cat['description'])) {
-                $category->description = $cat['description'];
-                $category->descriptionformat = external_validate_format($cat['descriptionformat']);
-            }
-            if (!empty($cat['theme'])) {
-                $category->theme = $cat['theme'];
-            }
-            if (!empty($cat['parent']) && ($category->parent != $cat['parent'])) {
-                // First check if parent exists.
-                if (!$parent_cat = $DB->get_record('course_categories', array('id' => $cat['parent']))) {
-                    throw new moodle_exception('unknowcategory');
-                }
-                // Then check if we have capability.
-                self::validate_context(get_category_or_system_context((int)$cat['parent']));
-                require_capability('moodle/category:manage', get_category_or_system_context((int)$cat['parent']));
-                // Finally move the category.
-                move_category($category, $parent_cat);
-                $category->parent = $cat['parent'];
-                // Get updated path by move_category().
-                $category->path = $DB->get_field('course_categories', 'path',
-                        array('id' => $category->id));
-            }
-            $DB->update_record('course_categories', $category);
+            // this will throw an exception if descriptionformat is not valid
+            external_validate_format($cat['descriptionformat']);
+
+            $category->update($cat);
         }
 
         $transaction->allow_commit();
@@ -1864,6 +1803,7 @@ class core_course_external extends external_api {
     public static function delete_categories($categories) {
         global $CFG, $DB;
         require_once($CFG->dirroot . "/course/lib.php");
+        require_once($CFG->libdir . "/coursecatlib.php");
 
         // Validate parameters.
         $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
@@ -1871,9 +1811,7 @@ class core_course_external extends external_api {
         $transaction = $DB->start_delegated_transaction();
 
         foreach ($params['categories'] as $category) {
-            if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
-                throw new moodle_exception('unknowcategory');
-            }
+            $deletecat = coursecat::get($category['id'], MUST_EXIST);
             $context = context_coursecat::instance($deletecat->id);
             require_capability('moodle/category:manage', $context);
             self::validate_context($context);
@@ -1881,29 +1819,32 @@ class core_course_external extends external_api {
 
             if ($category['recursive']) {
                 // If recursive was specified, then we recursively delete the category's contents.
-                category_delete_full($deletecat, false);
+                if ($deletecat->can_delete_full()) {
+                    $deletecat->delete_full(false);
+                } else {
+                    throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
+                }
             } else {
                 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
                 // If the parent is the root, moving is not supported (because a course must always be inside a category).
                 // We must move to an existing category.
                 if (!empty($category['newparent'])) {
-                    if (!$DB->record_exists('course_categories', array('id' => $category['newparent']))) {
-                        throw new moodle_exception('unknowcategory');
-                    }
-                    $newparent = $category['newparent'];
+                    $newparentcat = coursecat::get($category['newparent']);
                 } else {
-                    $newparent = $deletecat->parent;
+                    $newparentcat = coursecat::get($deletecat->parent);
                 }
 
                 // This operation is not allowed. We must move contents to an existing category.
-                if ($newparent == 0) {
+                if (!$newparentcat->id) {
                     throw new moodle_exception('movecatcontentstoroot');
                 }
 
-                $parentcontext = get_category_or_system_context($newparent);
-                require_capability('moodle/category:manage', $parentcontext);
-                self::validate_context($parentcontext);
-                category_delete_move($deletecat, $newparent, false);
+                self::validate_context(context_coursecat::instance($newparentcat->id));
+                if ($deletecat->can_move_content_to($newparentcat->id)) {
+                    $deletecat->delete_move($newparentcat->id, false);
+                } else {
+                    throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
+                }
             }
         }
 
index d9a7edc..6529716 100644 (file)
@@ -562,6 +562,39 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         return $o;
     }
 
+    /**
+     * Generate the html for the 'Jump to' menu on a single section page.
+     *
+     * @param stdClass $course The course entry from DB
+     * @param array $sections The course_sections entries from the DB
+     * @param $displaysection the current displayed section number.
+     *
+     * @return string HTML to output.
+     */
+    protected function section_nav_selection($course, $sections, $displaysection) {
+        global $CFG;
+        $o = '';
+        $sectionmenu = array();
+        $sectionmenu[course_get_url($course)->out(false)] = get_string('maincoursepage');
+        $modinfo = get_fast_modinfo($course);
+        $section = 1;
+        while ($section <= $course->numsections) {
+            $thissection = $modinfo->get_section_info($section);
+            $showsection = $thissection->uservisible or !$course->hiddensections;
+            if (($showsection) && ($section != $displaysection) && ($url = course_get_url($course, $section))) {
+                $sectionmenu[$url->out(false)] = get_section_name($course, $section);
+            }
+            $section++;
+        }
+
+        $select = new url_select($sectionmenu, '', array('' => get_string('jumpto')));
+        $select->class = 'jumpmenu';
+        $select->formid = 'sectionmenu';
+        $o .= $this->output->render($select);
+
+        return $o;
+    }
+
     /**
      * Output the html for a single section page .
      *
@@ -642,16 +675,16 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         echo $this->end_section_list();
 
         // Display section bottom navigation.
-        $courselink = html_writer::link(course_get_url($course), get_string('returntomaincoursepage'));
         $sectionbottomnav = '';
         $sectionbottomnav .= html_writer::start_tag('div', array('class' => 'section-navigation mdl-bottom'));
         $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
         $sectionbottomnav .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
-        $sectionbottomnav .= html_writer::tag('div', $courselink, array('class' => 'mdl-align'));
+        $sectionbottomnav .= html_writer::tag('div', $this->section_nav_selection($course, $sections, $displaysection),
+            array('class' => 'mdl-align'));
         $sectionbottomnav .= html_writer::end_tag('div');
         echo $sectionbottomnav;
 
-        // close single-section div.
+        // Close single-section div.
         echo html_writer::end_tag('div');
     }
 
index 347d17c..f906949 100644 (file)
@@ -1219,129 +1219,6 @@ function get_category_or_system_context($categoryid) {
     }
 }
 
-/**
- * Gets the child categories of a given courses category. Uses a static cache
- * to make repeat calls efficient.
- *
- * @param int $parentid the id of a course category.
- * @return array all the child course categories.
- */
-function get_child_categories($parentid) {
-    static $allcategories = null;
-
-    // only fill in this variable the first time
-    if (null == $allcategories) {
-        $allcategories = array();
-
-        $categories = get_categories();
-        foreach ($categories as $category) {
-            if (empty($allcategories[$category->parent])) {
-                $allcategories[$category->parent] = array();
-            }
-            $allcategories[$category->parent][] = $category;
-        }
-    }
-
-    if (empty($allcategories[$parentid])) {
-        return array();
-    } else {
-        return $allcategories[$parentid];
-    }
-}
-
-/**
- * This function recursively travels the categories, building up a nice list
- * for display. It also makes an array that list all the parents for each
- * category.
- *
- * For example, if you have a tree of categories like:
- *   Miscellaneous (id = 1)
- *      Subcategory (id = 2)
- *         Sub-subcategory (id = 4)
- *   Other category (id = 3)
- * Then after calling this function you will have
- * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory',
- *      4 => 'Miscellaneous / Subcategory / Sub-subcategory',
- *      3 => 'Other category');
- * $parents = array(2 => array(1), 4 => array(1, 2));
- *
- * If you specify $requiredcapability, then only categories where the current
- * user has that capability will be added to $list, although all categories
- * will still be added to $parents, and if you only have $requiredcapability
- * in a child category, not the parent, then the child catgegory will still be
- * included.
- *
- * If you specify the option $excluded, then that category, and all its children,
- * are omitted from the tree. This is useful when you are doing something like
- * moving categories, where you do not want to allow people to move a category
- * to be the child of itself.
- *
- * @param array $list For output, accumulates an array categoryid => full category path name
- * @param array $parents For output, accumulates an array categoryid => list of parent category ids.
- * @param string/array $requiredcapability if given, only categories where the current
- *      user has this capability will be added to $list. Can also be an array of capabilities,
- *      in which case they are all required.
- * @param integer $excludeid Omit this category and its children from the lists built.
- * @param object $category Build the tree starting at this category - otherwise starts at the top level.
- * @param string $path For internal use, as part of recursive calls.
- */
-function make_categories_list(&$list, &$parents, $requiredcapability = '',
-        $excludeid = 0, $category = NULL, $path = "") {
-
-    // initialize the arrays if needed
-    if (!is_array($list)) {
-        $list = array();
-    }
-    if (!is_array($parents)) {
-        $parents = array();
-    }
-
-    if (empty($category)) {
-        // Start at the top level.
-        $category = new stdClass;
-        $category->id = 0;
-    } else {
-        // This is the excluded category, don't include it.
-        if ($excludeid > 0 && $excludeid == $category->id) {
-            return;
-        }
-
-        $context = context_coursecat::instance($category->id);
-        $categoryname = format_string($category->name, true, array('context' => $context));
-
-        // Update $path.
-        if ($path) {
-            $path = $path.' / '.$categoryname;
-        } else {
-            $path = $categoryname;
-        }
-
-        // Add this category to $list, if the permissions check out.
-        if (empty($requiredcapability)) {
-            $list[$category->id] = $path;
-
-        } else {
-            $requiredcapability = (array)$requiredcapability;
-            if (has_all_capabilities($requiredcapability, $context)) {
-                $list[$category->id] = $path;
-            }
-        }
-    }
-
-    // Add all the children recursively, while updating the parents array.
-    if ($categories = get_child_categories($category->id)) {
-        foreach ($categories as $cat) {
-            if (!empty($category->id)) {
-                if (isset($parents[$category->id])) {
-                    $parents[$cat->id]   = $parents[$category->id];
-                }
-                $parents[$cat->id][] = $category->id;
-            }
-            make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path);
-        }
-    }
-}
-
 /**
  * This function generates a structured array of courses and categories.
  *
@@ -1358,14 +1235,14 @@ function make_categories_list(&$list, &$parents, $requiredcapability = '',
  */
 function get_course_category_tree($id = 0, $depth = 0) {
     global $DB, $CFG;
-    $viewhiddencats = has_capability('moodle/category:viewhiddencategories', context_system::instance());
-    $categories = get_child_categories($id);
+    require_once($CFG->libdir. '/coursecatlib.php');
+    if (!$coursecat = coursecat::get($id, IGNORE_MISSING)) {
+        return array();
+    }
+    $categories = array();
     $categoryids = array();
-    foreach ($categories as $key => &$category) {
-        if (!$category->visible && !$viewhiddencats) {
-            unset($categories[$key]);
-            continue;
-        }
+    foreach ($coursecat->get_children() as $child) {
+        $categories[] = $category = (object)convert_to_array($child);
         $categoryids[$category->id] = $category;
         if (empty($CFG->maxcategorydepth) || $depth <= $CFG->maxcategorydepth) {
             list($category->categories, $subcategories) = get_course_category_tree($category->id, $depth+1);
@@ -1417,37 +1294,31 @@ function get_course_category_tree($id = 0, $depth = 0) {
  */
 function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true, $categorycourses=NULL) {
     global $CFG;
+    require_once($CFG->libdir. '/coursecatlib.php');
 
     // maxcategorydepth == 0 meant no limit
     if (!empty($CFG->maxcategorydepth) && $depth >= $CFG->maxcategorydepth) {
         return;
     }
 
-    if (!$displaylist) {
-        make_categories_list($displaylist, $parentslist);
+    // make sure category is visible to the current user
+    if ($category) {
+        if (!$coursecat = coursecat::get($category->id, IGNORE_MISSING)) {
+            return;
+        }
+    } else {
+        $coursecat = coursecat::get(0);
     }
 
     if (!$categorycourses) {
-        if ($category) {
-            $categorycourses = get_category_courses_array($category->id);
-        } else {
-            $categorycourses = get_category_courses_array();
-        }
+        $categorycourses = get_category_courses_array($coursecat->id);
     }
 
-    if ($category) {
-        if ($category->visible or has_capability('moodle/category:viewhiddencategories', context_system::instance())) {
-            print_category_info($category, $depth, $showcourses, $categorycourses[$category->id]);
-        } else {
-            return;  // Don't bother printing children of invisible categories
-        }
-
-    } else {
-        $category = new stdClass();
-        $category->id = "0";
+    if ($coursecat->id) {
+        print_category_info($category, $depth, $showcourses, $categorycourses[$category->id]);
     }
 
-    if ($categories = get_child_categories($category->id)) {   // Print all the children recursively
+    if ($categories = $coursecat->get_children()) {   // Print all the children recursively
         $countcats = count($categories);
         $count = 0;
         $first = true;
@@ -1497,18 +1368,19 @@ function get_category_courses_array_recursively(array &$flattened, $category) {
 }
 
 /**
- * This function will return $options array for html_writer::select(), with whitespace to denote nesting.
+ * Returns full course categories trees to be used in html_writer::select()
+ *
+ * Calls {@link coursecat::make_categories_list()} to build the tree and
+ * adds whitespace to denote nesting
+ *
+ * @return array array mapping coursecat id to the display name
  */
 function make_categories_options() {
-    make_categories_list($cats,$parents);
+    global $CFG;
+    require_once($CFG->libdir. '/coursecatlib.php');
+    $cats = coursecat::make_categories_list();
     foreach ($cats as $key => $value) {
-        if (array_key_exists($key,$parents)) {
-            if ($indent = count($parents[$key])) {
-                for ($i = 0; $i < $indent; $i++) {
-                    $cats[$key] = '&nbsp;'.$cats[$key];
-                }
-            }
-        }
+        $cats[$key] = str_repeat('&nbsp;', coursecat::get($key)->depth - 1). $value;
     }
     return $cats;
 }
@@ -1672,9 +1544,10 @@ function can_edit_in_category($categoryid = 0) {
  */
 function print_courses($category) {
     global $CFG, $OUTPUT;
+    require_once($CFG->libdir. '/coursecatlib.php');
 
     if (!is_object($category) && $category==0) {
-        $categories = get_child_categories(0);  // Parent = 0   ie top-level categories only
+        $categories = coursecat::get(0)->get_children();  // Parent = 0   ie top-level categories only
         if (is_array($categories) && count($categories) == 1) {
             $category   = array_shift($categories);
             $courses    = get_courses_wmanagers($category->id,
@@ -2732,112 +2605,6 @@ function course_allowed_module($course, $modname) {
     return has_capability($capability, $coursecontext);
 }
 
-/**
- * Recursively delete category including all subcategories and courses.
- * @param stdClass $category
- * @param boolean $showfeedback display some notices
- * @return array return deleted courses
- */
-function category_delete_full($category, $showfeedback=true) {
-    global $CFG, $DB;
-    require_once($CFG->libdir.'/gradelib.php');
-    require_once($CFG->libdir.'/questionlib.php');
-    require_once($CFG->dirroot.'/cohort/lib.php');
-
-    if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
-        foreach ($children as $childcat) {
-            category_delete_full($childcat, $showfeedback);
-        }
-    }
-
-    $deletedcourses = array();
-    if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC')) {
-        foreach ($courses as $course) {
-            if (!delete_course($course, false)) {
-                throw new moodle_exception('cannotdeletecategorycourse','','',$course->shortname);
-            }
-            $deletedcourses[] = $course;
-        }
-    }
-
-    // move or delete cohorts in this context
-    cohort_delete_category($category);
-
-    // now delete anything that may depend on course category context
-    grade_course_category_delete($category->id, 0, $showfeedback);
-    if (!question_delete_course_category($category, 0, $showfeedback)) {
-        throw new moodle_exception('cannotdeletecategoryquestions','','',$category->name);
-    }
-
-    // finally delete the category and it's context
-    $DB->delete_records('course_categories', array('id'=>$category->id));
-    delete_context(CONTEXT_COURSECAT, $category->id);
-    add_to_log(SITEID, "category", "delete", "index.php", "$category->name (ID $category->id)");
-
-    events_trigger('course_category_deleted', $category);
-
-    return $deletedcourses;
-}
-
-/**
- * Delete category, but move contents to another category.
- * @param object $ccategory
- * @param int $newparentid category id
- * @return bool status
- */
-function category_delete_move($category, $newparentid, $showfeedback=true) {
-    global $CFG, $DB, $OUTPUT;
-    require_once($CFG->libdir.'/gradelib.php');
-    require_once($CFG->libdir.'/questionlib.php');
-    require_once($CFG->dirroot.'/cohort/lib.php');
-
-    if (!$newparentcat = $DB->get_record('course_categories', array('id'=>$newparentid))) {
-        return false;
-    }
-
-    if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) {
-        foreach ($children as $childcat) {
-            move_category($childcat, $newparentcat);
-        }
-    }
-
-    if ($courses = $DB->get_records('course', array('category'=>$category->id), 'sortorder ASC', 'id')) {
-        if (!move_courses(array_keys($courses), $newparentid)) {
-            if ($showfeedback) {
-                echo $OUTPUT->notification("Error moving courses");
-            }
-            return false;
-        }
-        if ($showfeedback) {
-            echo $OUTPUT->notification(get_string('coursesmovedout', '', format_string($category->name)), 'notifysuccess');
-        }
-    }
-
-    // move or delete cohorts in this context
-    cohort_delete_category($category);
-
-    // now delete anything that may depend on course category context
-    grade_course_category_delete($category->id, $newparentid, $showfeedback);
-    if (!question_delete_course_category($category, $newparentcat, $showfeedback)) {
-        if ($showfeedback) {
-            echo $OUTPUT->notification(get_string('errordeletingquestionsfromcategory', 'question', $category), 'notifysuccess');
-        }
-        return false;
-    }
-
-    // finally delete the category and it's context
-    $DB->delete_records('course_categories', array('id'=>$category->id));
-    delete_context(CONTEXT_COURSECAT, $category->id);
-    add_to_log(SITEID, "category", "delete", "index.php", "$category->name (ID $category->id)");
-
-    events_trigger('course_category_deleted', $category);
-
-    if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('coursecategorydeleted', '', format_string($category->name)), 'notifysuccess');
-    }
-    return true;
-}
-
 /**
  * Efficiently moves many courses around while maintaining
  * sortorder in order.
@@ -2882,98 +2649,11 @@ function move_courses($courseids, $categoryid) {
         }
     }
     fix_course_sortorder();
+    cache_helper::purge_by_event('changesincourse');
 
     return true;
 }
 
-/**
- * Hide course category and child course and subcategories
- * @param stdClass $category
- * @return void
- */
-function course_category_hide($category) {
-    global $DB;
-
-    $category->visible = 0;
-    $DB->set_field('course_categories', 'visible', 0, array('id'=>$category->id));
-    $DB->set_field('course_categories', 'visibleold', 0, array('id'=>$category->id));
-    $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($category->id)); // store visible flag so that we can return to it if we immediately unhide
-    $DB->set_field('course', 'visible', 0, array('category' => $category->id));
-    // get all child categories and hide too
-    if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
-        foreach ($subcats as $cat) {
-            $DB->set_field('course_categories', 'visibleold', $cat->visible, array('id'=>$cat->id));
-            $DB->set_field('course_categories', 'visible', 0, array('id'=>$cat->id));
-            $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($cat->id));
-            $DB->set_field('course', 'visible', 0, array('category' => $cat->id));
-        }
-    }
-    add_to_log(SITEID, "category", "hide", "editcategory.php?id=$category->id", $category->id);
-}
-
-/**
- * Show course category and child course and subcategories
- * @param stdClass $category
- * @return void
- */
-function course_category_show($category) {
-    global $DB;
-
-    $category->visible = 1;
-    $DB->set_field('course_categories', 'visible', 1, array('id'=>$category->id));
-    $DB->set_field('course_categories', 'visibleold', 1, array('id'=>$category->id));
-    $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($category->id));
-    // get all child categories and unhide too
-    if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
-        foreach ($subcats as $cat) {
-            if ($cat->visibleold) {
-                $DB->set_field('course_categories', 'visible', 1, array('id'=>$cat->id));
-            }
-            $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($cat->id));
-        }
-    }
-    add_to_log(SITEID, "category", "show", "editcategory.php?id=$category->id", $category->id);
-}
-
-/**
- * Efficiently moves a category - NOTE that this can have
- * a huge impact access-control-wise...
- */
-function move_category($category, $newparentcat) {
-    global $CFG, $DB;
-
-    $context = context_coursecat::instance($category->id);
-
-    $hidecat = false;
-    if (empty($newparentcat->id)) {
-        $DB->set_field('course_categories', 'parent', 0, array('id' => $category->id));
-        $newparent = context_system::instance();
-    } else {
-        $DB->set_field('course_categories', 'parent', $newparentcat->id, array('id' => $category->id));
-        $newparent = context_coursecat::instance($newparentcat->id);
-
-        if (!$newparentcat->visible and $category->visible) {
-            // better hide category when moving into hidden category, teachers may unhide afterwards and the hidden children will be restored properly
-            $hidecat = true;
-        }
-    }
-
-    context_moved($context, $newparent);
-
-    // now make it last in new category
-    $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('id'=>$category->id));
-
-    // Log action.
-    add_to_log(SITEID, "category", "move", "editcategory.php?id=$category->id", $category->id);
-
-    // and fix the sortorders
-    fix_course_sortorder();
-
-    if ($hidecat) {
-        course_category_hide($category);
-    }
-}
-
 /**
  * Returns the display name of the given section that the course prefers
  *
@@ -3149,6 +2829,8 @@ function create_course($data, $editoroptions = NULL) {
     course_create_sections_if_missing($course, 0);
 
     fix_course_sortorder();
+    // purge appropriate caches in case fix_course_sortorder() did not change anything
+    cache_helper::purge_by_event('changesincourse');
 
     // new context created - better mark it as dirty
     mark_context_dirty($context->path);
@@ -3167,32 +2849,6 @@ function create_course($data, $editoroptions = NULL) {
     return $course;
 }
 
-/**
- * Create a new course category and marks the context as dirty
- *
- * This function does not set the sortorder for the new category and
- * @see{fix_course_sortorder} should be called after creating a new course
- * category
- *
- * Please note that this function does not verify access control.
- *
- * @param object $category All of the data required for an entry in the course_categories table
- * @return object new course category
- */
-function create_course_category($category) {
-    global $DB;
-
-    $category->timemodified = time();
-    $category->id = $DB->insert_record('course_categories', $category);
-    $category = $DB->get_record('course_categories', array('id' => $category->id));
-
-    // We should mark the context as dirty
-    $category->context = context_coursecat::instance($category->id);
-    $category->context->mark_dirty();
-
-    return $category;
-}
-
 /**
  * Update a course.
  *
@@ -3255,6 +2911,8 @@ function update_course($data, $editoroptions = NULL) {
     }
 
     fix_course_sortorder();
+    // purge appropriate caches in case fix_course_sortorder() did not change anything
+    cache_helper::purge_by_event('changesincourse');
 
     // Test for and remove blocks which aren't appropriate anymore
     blocks_remove_inappropriate($course);
@@ -3555,6 +3213,31 @@ class course_request {
         return $this->properties->collision;
     }
 
+    /**
+     * Returns the category where this course request should be created
+     *
+     * Note that we don't check here that user has a capability to view
+     * hidden categories if he has capabilities 'moodle/site:approvecourse' and
+     * 'moodle/course:changecategory'
+     *
+     * @return coursecat
+     */
+    public function get_category() {
+        global $CFG;
+        require_once($CFG->libdir.'/coursecatlib.php');
+        // If the category is not set, if the current user does not have the rights to change the category, or if the
+        // category does not exist, we set the default category to the course to be approved.
+        // The system level is used because the capability moodle/site:approvecourse is based on a system level.
+        if (empty($this->properties->category) || !has_capability('moodle/course:changecategory', context_system::instance()) ||
+                (!$category = coursecat::get($this->properties->category, IGNORE_MISSING, true))) {
+            $category = coursecat::get($CFG->defaultrequestcategory, IGNORE_MISSING, true);
+        }
+        if (!$category) {
+            $category = coursecat::get_default();
+        }
+        return $category;
+    }
+
     /**
      * This function approves the request turning it into a course
      *
@@ -3577,18 +3260,9 @@ class course_request {
         unset($data->reason);
         unset($data->requester);
 
-        // If the category is not set, if the current user does not have the rights to change the category, or if the
-        // category does not exist, we set the default category to the course to be approved.
-        // The system level is used because the capability moodle/site:approvecourse is based on a system level.
-        if (empty($data->category) || !has_capability('moodle/course:changecategory', context_system::instance()) ||
-                (!$category = get_course_category($data->category))) {
-            $category = get_course_category($CFG->defaultrequestcategory);
-        }
-
         // Set category
+        $category = $this->get_category();
         $data->category = $category->id;
-        $data->sortorder = $category->sortorder; // place as the first in category
-
         // Set misc settings
         $data->requested = 1;
 
@@ -3972,3 +3646,55 @@ function update_module($moduleinfo) {
 
     return $moduleinfo;
 }
+
+/**
+ * Compare two objects to find out their correct order based on timestamp (to be used by usort).
+ * Sorts by descending order of time.
+ *
+ * @param stdClass $a First object
+ * @param stdClass $b Second object
+ * @return int 0,1,-1 representing the order
+ */
+function compare_activities_by_time_desc($a, $b) {
+    // Make sure the activities actually have a timestamp property.
+    if ((!property_exists($a, 'timestamp')) && (!property_exists($b, 'timestamp'))) {
+        return 0;
+    }
+    // We treat instances without timestamp as if they have a timestamp of 0.
+    if ((!property_exists($a, 'timestamp')) && (property_exists($b,'timestamp'))) {
+        return 1;
+    }
+    if ((property_exists($a, 'timestamp')) && (!property_exists($b, 'timestamp'))) {
+        return -1;
+    }
+    if ($a->timestamp == $b->timestamp) {
+        return 0;
+    }
+    return ($a->timestamp > $b->timestamp) ? -1 : 1;
+}
+
+/**
+ * Compare two objects to find out their correct order based on timestamp (to be used by usort).
+ * Sorts by ascending order of time.
+ *
+ * @param stdClass $a First object
+ * @param stdClass $b Second object
+ * @return int 0,1,-1 representing the order
+ */
+function compare_activities_by_time_asc($a, $b) {
+    // Make sure the activities actually have a timestamp property.
+    if ((!property_exists($a, 'timestamp')) && (!property_exists($b, 'timestamp'))) {
+      return 0;
+    }
+    // We treat instances without timestamp as if they have a timestamp of 0.
+    if ((!property_exists($a, 'timestamp')) && (property_exists($b, 'timestamp'))) {
+        return -1;
+    }
+    if ((property_exists($a, 'timestamp')) && (!property_exists($b, 'timestamp'))) {
+        return 1;
+    }
+    if ($a->timestamp == $b->timestamp) {
+        return 0;
+    }
+    return ($a->timestamp < $b->timestamp) ? -1 : 1;
+}
index 909ce78..128e7a7 100644 (file)
@@ -24,6 +24,7 @@
 
 require_once("../config.php");
 require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->libdir.'/coursecatlib.php');
 
 // Category id.
 $id = optional_param('id', 0, PARAM_INT);
@@ -51,18 +52,17 @@ $movedowncat = optional_param('movedowncat', 0, PARAM_INT);
 
 require_login();
 
+// Retrieve coursecat object
+// This will also make sure that category is accessible and create default category if missing
+$coursecat = coursecat::get($id);
+
 if ($id) {
     $PAGE->set_category_by_id($id);
     $PAGE->set_url(new moodle_url('/course/manage.php', array('id' => $id)));
     // This is sure to be the category context.
     $context = $PAGE->context;
-    // And the object has been loaded for us no need for another DB call.
-    $category = $PAGE->category;
-    if (!can_edit_in_category($category->id)) {
-        redirect(new moodle_url('/course/category.php', array('id' => $category->id)));
-    }
-    if (!$category->visible) {
-        require_capability('moodle/category:viewhiddencategories', $context);
+    if (!can_edit_in_category($coursecat->id)) {
+        redirect(new moodle_url('/course/category.php', array('id' => $coursecat->id)));
     }
 } else {
     $context = context_system::instance();
@@ -75,26 +75,10 @@ if ($id) {
 
 $canmanage = has_capability('moodle/category:manage', $context);
 
-// Check the default category exists.
-if (!$id && !$DB->record_exists('course_categories', array('parent' => 0))) {
-    // No category yet! Try and make one.
-    $tempcat = new stdClass;
-    $tempcat->name = get_string('miscellaneous');
-    $tempcat->id = $DB->insert_record('course_categories', $tempcat);
-    // Fetch the context to ensure it is created.
-    context_coursecat::instance($tempcat->id);
-    mark_context_dirty('/'.SYSCONTEXTID);
-    // Required to build course_categories.depth and categories.path.
-    fix_course_sortorder();
-    set_config('defaultrequestcategory', $tempcat->id);
-    // Unset the temp category. We no longer need it.
-    unset($tempcat);
-}
-
 // Process any category actions.
 if (!empty($deletecat) and confirm_sesskey()) {
     // Delete a category.
-    $cattodelete = $DB->get_record('course_categories', array('id' => $deletecat), '*', MUST_EXIST);
+    $cattodelete = coursecat::get($deletecat);
     $context = context_coursecat::instance($deletecat);
     require_capability('moodle/category:manage', $context);
     require_capability('moodle/category:manage', get_category_or_system_context($cattodelete->parent));
@@ -103,7 +87,6 @@ if (!empty($deletecat) and confirm_sesskey()) {
 
     require_once($CFG->dirroot.'/course/delete_category_form.php');
     $mform = new delete_category_form(null, $cattodelete);
-    $mform->set_data(array('deletecat' => $deletecat));
     if ($mform->is_cancelled()) {
         redirect(new moodle_url('/course/manage.php'));
     }
@@ -114,25 +97,24 @@ if (!empty($deletecat) and confirm_sesskey()) {
 
     if ($data = $mform->get_data()) {
         // The form has been submit handle it.
-        if ($data->fulldelete) {
-            $deletedcourses = category_delete_full($cattodelete, true);
+        if ($data->fulldelete == 1 && $cattodelete->can_delete_full()) {
+            $cattodeletename = $cattodelete->get_formatted_name();
+            $deletedcourses = $cattodelete->delete_full(true);
             foreach ($deletedcourses as $course) {
                 echo $OUTPUT->notification(get_string('coursedeleted', '', $course->shortname), 'notifysuccess');
             }
-            $cattodeletename = format_string($cattodelete->name, true, array('context' => $context));
             echo $OUTPUT->notification(get_string('coursecategorydeleted', '', $cattodeletename), 'notifysuccess');
+            echo $OUTPUT->continue_button(new moodle_url('/course/manage.php'));
 
+        } else if ($data->fulldelete == 0 && $cattodelete->can_move_content_to($data->newparent)) {
+            $cattodelete->delete_move($data->newparent, true);
+            echo $OUTPUT->continue_button(new moodle_url('/course/manage.php'));
         } else {
-            category_delete_move($cattodelete, $data->newparent, true);
-        }
-        if ($deletecat == $CFG->defaultrequestcategory) {
-            // If we deleted $CFG->defaultrequestcategory, make it point somewhere else.
-            set_config('defaultrequestcategory', $DB->get_field('course_categories', 'MIN(id)', array('parent' => 0)));
+            // Some error in parameters (user is cheating?)
+            $mform->display();
         }
-        echo $OUTPUT->continue_button(new moodle_url('/course/manage.php'));
     } else {
         // Display the form.
-        require_once($CFG->libdir . '/questionlib.php');
         $mform->display();
     }
     // Finish output and exit.
@@ -142,31 +124,25 @@ if (!empty($deletecat) and confirm_sesskey()) {
 
 if (!empty($movecat) and ($movetocat >= 0) and confirm_sesskey()) {
     // Move a category to a new parent if required.
-    if ($cattomove = $DB->get_record('course_categories', array('id' => $movecat))) {
-        require_capability('moodle/category:manage', get_category_or_system_context($cattomove->parent));
-        if ($cattomove->parent != $movetocat) {
-            $newparent = $DB->get_record('course_categories', array('id' => $movetocat));
-            require_capability('moodle/category:manage', get_category_or_system_context($movetocat));
-            move_category($cattomove, $newparent);
+    $cattomove = coursecat::get($movecat);
+    if ($cattomove->parent != $movetocat) {
+        if ($cattomove->can_change_parent($movetocat)) {
+            $cattomove->change_parent($movetocat);
+        } else {
+            print_error('cannotmovecategory');
         }
     }
 }
 
 // Hide or show a category.
 if ($hidecat and confirm_sesskey()) {
-    if ($tempcat = $DB->get_record('course_categories', array('id' => $hidecat))) {
-        require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
-        if ($tempcat->visible == 1) {
-            course_category_hide($tempcat);
-        }
-    }
+    $cattohide = coursecat::get($hidecat);
+    require_capability('moodle/category:manage', get_category_or_system_context($cattohide->parent));
+    $cattohide->hide();
 } else if ($showcat and confirm_sesskey()) {
-    if ($tempcat = $DB->get_record('course_categories', array('id' => $showcat))) {
-        require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
-        if ($tempcat->visible == 0) {
-            course_category_show($tempcat);
-        }
-    }
+    $cattoshow = coursecat::get($showcat);
+    require_capability('moodle/category:manage', get_category_or_system_context($cattoshow->parent));
+    $cattoshow->show();
 }
 
 if ((!empty($moveupcat) or !empty($movedowncat)) and confirm_sesskey()) {
@@ -201,17 +177,18 @@ if ((!empty($moveupcat) or !empty($movedowncat)) and confirm_sesskey()) {
     fix_course_sortorder();
 }
 
-if (isset($category) && $canmanage && $resort && confirm_sesskey()) {
+if ($coursecat->id && $canmanage && $resort && confirm_sesskey()) {
     // Resort the category.
-    if ($courses = get_courses($category->id, '', 'c.id,c.fullname,c.sortorder')) {
+    if ($courses = get_courses($coursecat->id, '', 'c.id,c.fullname,c.sortorder')) {
         collatorlib::asort_objects_by_property($courses, 'fullname', collatorlib::SORT_NATURAL);
         $i = 1;
         foreach ($courses as $course) {
-            $DB->set_field('course', 'sortorder', $category->sortorder + $i, array('id' => $course->id));
+            $DB->set_field('course', 'sortorder', $coursecat->sortorder + $i, array('id' => $course->id));
             $i++;
         }
         // This should not be needed but we do it just to be safe.
         fix_course_sortorder();
+        cache_helper::purge_by_event('changesincourse');
     }
 }
 
@@ -257,6 +234,7 @@ if ((!empty($hide) or !empty($show)) && confirm_sesskey()) {
     // Set the visibility of the course. we set the old flag when user manually changes visibility of course.
     $params = array('id' => $course->id, 'visible' => $visible, 'visibleold' => $visible, 'timemodified' => time());
     $DB->update_record('course', $params);
+    cache_helper::purge_by_event('changesincourse');
     add_to_log($course->id, "course", ($visible ? 'show' : 'hide'), "edit.php?id=$course->id", $course->id);
 }
 
@@ -284,6 +262,7 @@ if ((!empty($moveup) or !empty($movedown)) && confirm_sesskey()) {
         }
         $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id));
         $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id));
+        cache_helper::purge_by_event('changesincourse');
         add_to_log($movecourse->id, "course", "move", "edit.php?id=$movecourse->id", $movecourse->id);
     }
 }
@@ -314,24 +293,18 @@ if (can_edit_in_category()) {
         $PAGE->navbar->add($settingsnode->text, $settingsnode->action);
     }
 } else {
-    // If we get here then they must have arrived here using a specific category
-    // within which they can manage.
-    // We can safetly assume $category is set.
     $site = get_site();
-    $PAGE->set_title("$site->shortname: $category->name");
+    $PAGE->set_title("$site->shortname: $coursecat->name");
     $PAGE->set_heading($site->fullname);
     $PAGE->set_button(print_course_search('', true, 'navbar'));
 }
 
-$parentlist = array();
-$displaylist = array();
-make_categories_list($displaylist, $parentlist);
 $displaylist[0] = get_string('top');
 
 // Start output.
 echo $OUTPUT->header();
 
-if (!isset($category)) {
+if (!$coursecat->id) {
     // Print out the categories with all the knobs.
     $table = new html_table;
     $table->id = 'coursecategories';
@@ -350,12 +323,12 @@ if (!isset($category)) {
     );
     $table->data = array();
 
-    print_category_edit($table, null, $displaylist, $parentlist);
+    print_category_edit($table, $coursecat);
 
     echo html_writer::table($table);
 } else {
     // Print the category selector.
-    $select = new single_select(new moodle_url('/course/manage.php'), 'id', $displaylist, $category->id, null, 'switchcategory');
+    $select = new single_select(new moodle_url('/course/manage.php'), 'id', $displaylist, $coursecat->id, null, 'switchcategory');
     $select->set_label(get_string('categories').':');
 
     echo html_writer::start_tag('div', array('class' => 'categorypicker'));
@@ -382,7 +355,7 @@ if ($canmanage) {
     echo $OUTPUT->container_end();
 }
 
-if (isset($category)) {
+if ($coursecat->id) {
     // Print out all the sub-categories (plain mode).
     // In order to view hidden subcategories the user must have the viewhiddencategories.
     // capability in the current category..
@@ -401,7 +374,7 @@ if (isset($category)) {
                    ctx.contextlevel = :contextlevel
                    $categorywhere
           ORDER BY cc.sortorder ASC";
-    $subcategories = $DB->get_recordset_sql($sql, array('parentid' => $category->id, 'contextlevel' => CONTEXT_COURSECAT));
+    $subcategories = $DB->get_recordset_sql($sql, array('parentid' => $coursecat->id, 'contextlevel' => CONTEXT_COURSECAT));
     // Prepare a table to display the sub categories.
     $table = new html_table;
     $table->attributes = array(
@@ -430,7 +403,7 @@ if (isset($category)) {
         echo html_writer::table($table);
     }
 
-    $courses = get_courses_page($category->id, 'c.sortorder ASC',
+    $courses = get_courses_page($coursecat->id, 'c.sortorder ASC',
             'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible',
             $totalcount, $page*$perpage, $perpage);
     $numcourses = count($courses);
@@ -552,9 +525,7 @@ if (!$courses) {
     }
 
     if ($abletomovecourses) {
-        $movetocategories = array();
-        $notused = array();
-        make_categories_list($movetocategories, $notused, 'moodle/category:manage');
+        $movetocategories = coursecat::make_categories_list('moodle/category:manage');
         $movetocategories[$id] = get_string('moveselectedcoursesto');
 
         $cell = new html_table_cell();
@@ -593,8 +564,8 @@ if ($canmanage and $numcourses > 1) {
 if (has_capability('moodle/course:create', $context)) {
     // Print button to create a new course.
     $url = new moodle_url('/course/edit.php');
-    if (isset($category)) {
-        $url->params(array('category' => $category->id, 'returnto' => 'catmanage'));
+    if ($coursecat->id) {
+        $url->params(array('category' => $coursecat->id, 'returnto' => 'catmanage'));
     } else {
         $url->params(array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcatmanage'));
     }
@@ -614,14 +585,12 @@ echo $OUTPUT->footer();
  * Recursive function to print all the categories ready for editing.
  *
  * @param html_table $table The table to add data to.
- * @param stdClass $category The category to render
- * @param array $displaylist The categories this can be moved to.
- * @param array $parentslist An array of categories.
+ * @param coursecat $category The category to render
  * @param int $depth The depth of the category.
  * @param bool $up True if this category can be moved up.
  * @param bool $down True if this category can be moved down.
  */
-function print_category_edit(html_table $table, $category, $displaylist, $parentslist, $depth=-1, $up=false, $down=false) {
+function print_category_edit(html_table $table, coursecat $category, $depth = -1, $up = false, $down = false) {
     global $OUTPUT;
 
     static $str = null;
@@ -639,22 +608,20 @@ function print_category_edit(html_table $table, $category, $displaylist, $parent
         $str->spacer = $OUTPUT->spacer().' ';
     }
 
-    if (!empty($category)) {
+    if ($category->id) {
 
-        if (!isset($category->context)) {
-            $category->context = context_coursecat::instance($category->id);
-        }
+        $categorycontext = context_coursecat::instance($category->id);
 
         $attributes = array();
         $attributes['class'] = $category->visible ? '' : 'dimmed';
         $attributes['title'] = $str->edit;
         $categoryurl = new moodle_url('/course/manage.php', array('id' => $category->id, 'sesskey' => sesskey()));
-        $categoryname = format_string($category->name, true, array('context' => $category->context));
+        $categoryname = $category->get_formatted_name();
         $categorypadding = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', $depth);
         $categoryname = $categorypadding . html_writer::link($categoryurl, $categoryname, $attributes);
 
         $icons = array();
-        if (has_capability('moodle/category:manage', $category->context)) {
+        if (has_capability('moodle/category:manage', $categorycontext)) {
             // Edit category.
             $icons[] = $OUTPUT->action_icon(
                 new moodle_url('/course/editcategory.php', array('id' => $category->id)),
@@ -682,9 +649,9 @@ function print_category_edit(html_table $table, $category, $displaylist, $parent
                 );
             }
             // Cohorts.
-            if (has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:view'), $category->context)) {
+            if (has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:view'), $categorycontext)) {
                 $icons[] = $OUTPUT->action_icon(
-                    new moodle_url('/cohort/index.php', array('contextid' => $category->context->id)),
+                    new moodle_url('/cohort/index.php', array('contextid' => $categorycontext->id)),
                     new pix_icon('t/cohort', $str->cohorts, 'moodle', array('class' => 'iconsmall')),
                     null, array('title' => $str->cohorts)
                 );
@@ -711,15 +678,9 @@ function print_category_edit(html_table $table, $category, $displaylist, $parent
         }
 
         $actions = '';
-        if (has_capability('moodle/category:manage', $category->context)) {
-            $tempdisplaylist = $displaylist;
-            unset($tempdisplaylist[$category->id]);
-            foreach ($parentslist as $key => $parents) {
-                if (in_array($category->id, $parents)) {
-                    unset($tempdisplaylist[$key]);
-                }
-            }
+        if (has_capability('moodle/category:manage', $categorycontext)) {
             $popupurl = new moodle_url("manage.php?movecat=$category->id&sesskey=".sesskey());
+            $tempdisplaylist = array(0 => get_string('top')) + coursecat::make_categories_list('moodle/category:manage', $category->id);
             $select = new single_select($popupurl, 'movetocat', $tempdisplaylist, $category->parent, null, "moveform$category->id");
             $select->set_label(get_string('frontpagecategorynames'), array('class' => 'accesshide'));
             $actions = $OUTPUT->render($select);
@@ -735,14 +696,9 @@ function print_category_edit(html_table $table, $category, $displaylist, $parent
             // Actions.
             new html_table_cell($actions)
         ));
-
-        // Get the subcategories to be printed.
-        $categories = get_categories($category->id);
-    } else {
-        $categories = get_categories(0);
     }
 
-    if ($categories) {
+    if ($categories = $category->get_children()) {
         // Print all the children recursively.
         $countcats = count($categories);
         $count = 0;
@@ -757,7 +713,7 @@ function print_category_edit(html_table $table, $category, $displaylist, $parent
             $down = $last ? false : true;
             $first = false;
 
-            print_category_edit($table, $cat, $displaylist, $parentslist, $depth+1, $up, $down);
+            print_category_edit($table, $cat, $depth+1, $up, $down);
         }
     }
 }
index 201f484..c837896 100644 (file)
@@ -457,6 +457,21 @@ abstract class moodleform_mod extends moodleform {
         //$this->standard_grading_coursemodule_elements();
 
         $mform->addElement('header', 'modstandardelshdr', get_string('modstandardels', 'form'));
+
+        $mform->addElement('modvisible', 'visible', get_string('visible'));
+        if (!empty($this->_cm)) {
+            $context = context_module::instance($this->_cm->id);
+            if (!has_capability('moodle/course:activityvisibility', $context)) {
+                $mform->hardFreeze('visible');
+            }
+        }
+
+        if ($this->_features->idnumber) {
+            $mform->addElement('text', 'cmidnumber', get_string('idnumbermod'));
+            $mform->setType('cmidnumber', PARAM_RAW);
+            $mform->addHelpButton('cmidnumber', 'idnumbermod');
+        }
+
         if ($this->_features->groups) {
             $options = array(NOGROUPS       => get_string('groupsnone'),
                              SEPARATEGROUPS => get_string('groupsseparate'),
@@ -476,26 +491,11 @@ abstract class moodleform_mod extends moodleform {
             }
             $mform->addElement('select', 'groupingid', get_string('grouping', 'group'), $options);
             $mform->addHelpButton('groupingid', 'grouping', 'group');
-            $mform->setAdvanced('groupingid');
         }
 
         if ($this->_features->groupmembersonly) {
             $mform->addElement('checkbox', 'groupmembersonly', get_string('groupmembersonly', 'group'));
             $mform->addHelpButton('groupmembersonly', 'groupmembersonly', 'group');
-            $mform->setAdvanced('groupmembersonly');
-        }
-
-        $mform->addElement('modvisible', 'visible', get_string('visible'));
-        if (!empty($this->_cm)) {
-            $context = context_module::instance($this->_cm->id);
-            if (!has_capability('moodle/course:activityvisibility', $context)) {
-                $mform->hardFreeze('visible');
-            }
-        }
-
-        if ($this->_features->idnumber) {
-            $mform->addElement('text', 'cmidnumber', get_string('idnumbermod'));
-            $mform->addHelpButton('cmidnumber', 'idnumbermod');
         }
 
         if (!empty($CFG->enableavailability)) {
index 3d6cb18..eaf4c28 100644 (file)
@@ -110,23 +110,14 @@ if (empty($pending)) {
         // Check here for shortname collisions and warn about them.
         $course->check_shortname_collision();
 
-        // Retreiving category name.
-        // If the category was not set (can happen after upgrade) or if the user does not have the capability
-        // to change the category, we fallback on the default one.
-        // Else, the category proposed is fetched, but we fallback on the default one if we can't find it.
-        // It is just a matter of displaying the right information because the logic when approving the category
-        // proceeds the same way. The system context level is used as moodle/site:approvecourse uses it.
-        if (empty($course->category) || !has_capability('moodle/course:changecategory', context_system::instance()) ||
-                (!$category = get_course_category($course->category))) {
-            $category = get_course_category($CFG->defaultrequestcategory);
-        }
+        $category = $course->get_category();
 
         $row = array();
         $row[] = format_string($course->shortname);
         $row[] = format_string($course->fullname);
         $row[] = fullname($course->get_requester());
         $row[] = $course->summary;
-        $row[] = format_string($category->name);
+        $row[] = $category->get_formatted_name();
         $row[] = format_string($course->reason);
         $row[] = $OUTPUT->single_button(new moodle_url($baseurl, array('approve' => $course->id, 'sesskey' => sesskey())), get_string('approve'), 'get') .
                  $OUTPUT->single_button(new moodle_url($baseurl, array('reject' => $course->id)), get_string('rejectdots'), 'get');
index 8640419..192ccc2 100644 (file)
@@ -30,6 +30,7 @@ require_once('recent_form.php');
 $id = required_param('id', PARAM_INT);
 
 $PAGE->set_url('/course/recent.php', array('id'=>$id));
+$PAGE->set_pagelayout('report');
 
 if (!$course = $DB->get_record('course', array('id'=>$id))) {
     print_error("That's an invalid course id");
@@ -272,39 +273,3 @@ if (!empty($activities)) {
 
 echo $OUTPUT->footer();
 
-function compare_activities_by_time_desc($a, $b) {
-    // make sure the activities actually have a timestamp property
-    if ((!array_key_exists('timestamp', $a)) && (!array_key_exists('timestamp', $b))) {
-        return 0;
-    }
-    // We treat instances without timestamp as if they have a timestamp of 0.
-    if ((!array_key_exists('timestamp', $a)) && (array_key_exists('timestamp', $b))) {
-        return 1;
-    }
-    if ((array_key_exists('timestamp', $a)) && (!array_key_exists('timestamp', $b))) {
-        return -1;
-    }
-    if ($a->timestamp == $b->timestamp) {
-        return 0;
-    }
-    return ($a->timestamp > $b->timestamp) ? -1 : 1;
-}
-
-function compare_activities_by_time_asc($a, $b) {
-    // make sure the activities actually have a timestamp property
-    if ((!array_key_exists('timestamp', $a)) && (!array_key_exists('timestamp', $b))) {
-      return 0;
-    }
-    // We treat instances without timestamp as if they have a timestamp of 0.
-    if ((!array_key_exists('timestamp', $a)) && (array_key_exists('timestamp', $b))) {
-        return -1;
-    }
-    if ((array_key_exists('timestamp', $a)) && (!array_key_exists('timestamp', $b))) {
-        return 1;
-    }
-    if ($a->timestamp == $b->timestamp) {
-        return 0;
-    }
-    return ($a->timestamp < $b->timestamp) ? -1 : 1;
-}
-
index ab89e16..54b3244 100644 (file)
@@ -649,7 +649,8 @@ class core_course_renderer extends plugin_renderer_base {
                 $output .= html_writer::empty_tag('input', array(
                     'type' => 'image',
                     'src' => $this->output->pix_url('i/completion-'.$completionicon),
-                    'alt' => $imgalt, 'title' => $imgtitle));
+                    'alt' => $imgalt, 'title' => $imgtitle,
+                    'aria-live' => 'polite'));
                 $output .= html_writer::end_tag('div');
                 $output .= html_writer::end_tag('form');
             } else {
index 867a684..620284c 100644 (file)
@@ -36,6 +36,7 @@ if (!defined('MOODLE_INTERNAL')) {
 }
 
 require_once($CFG->libdir.'/formslib.php');
+require_once($CFG->libdir.'/coursecatlib.php');
 
 /**
  * A form for a user to request a course.
@@ -69,9 +70,7 @@ class course_request_form extends moodleform {
         $mform->setType('shortname', PARAM_TEXT);
 
         if (!empty($CFG->requestcategoryselection)) {
-            $displaylist = array();
-            $parentlist = array();
-            make_categories_list($displaylist, $parentlist, '');
+            $displaylist = coursecat::make_categories_list();
             $mform->addElement('select', 'category', get_string('category'), $displaylist);
             $mform->setDefault('category', $CFG->defaultrequestcategory);
             $mform->addHelpButton('category', 'category');
index aeccc4f..38bac69 100644 (file)
@@ -24,6 +24,7 @@
 
 require_once("../config.php");
 require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->libdir.'/coursecatlib.php');
 
 $search    = optional_param('search', '', PARAM_RAW);  // search words
 $page      = optional_param('page', 0, PARAM_INT);     // which page to show
@@ -38,14 +39,8 @@ $modulelist= optional_param('modulelist', '', PARAM_PLUGIN);
 // List of minimum capabilities which user need to have for editing/moving course
 $capabilities = array('moodle/course:create', 'moodle/category:manage');
 
-// List of category id's in which current user has course:create and category:manage capability.
-$usercatlist = array();
-
-// List of parent category id's
-$catparentlist = array();
-
-// Populate usercatlist with list of category id's with required capabilities.
-make_categories_list($usercatlist, $catparentlist, $capabilities);
+// Populate usercatlist with list of category id's with course:create and category:manage capabilities.
+$usercatlist = coursecat::make_categories_list($capabilities);
 
 $search = trim(strip_tags($search)); // trim & clean raw searched string
 if ($search) {
@@ -109,9 +104,7 @@ if (has_capability('moodle/course:visibility', context_system::instance())) {
     }
 }
 
-$displaylist = array();
-$parentlist = array();
-make_categories_list($displaylist, $parentlist);
+$displaylist = coursecat::make_categories_list();
 
 $strcourses = new lang_string("courses");
 $strsearch = new lang_string("search");
index 6fc792a..9d08906 100644 (file)
@@ -785,54 +785,6 @@ class courselib_testcase extends advanced_testcase {
         $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course));
     }
 
-    public function test_create_course_category() {
-        global $CFG, $DB;
-        $this->resetAfterTest(true);
-
-        // Create the category
-        $data = new stdClass();
-        $data->name = 'aaa';
-        $data->description = 'aaa';
-        $data->idnumber = '';
-
-        $category1 = create_course_category($data);
-
-        // Initially confirm that base data was inserted correctly
-        $this->assertEquals($data->name, $category1->name);
-        $this->assertEquals($data->description, $category1->description);
-        $this->assertEquals($data->idnumber, $category1->idnumber);
-
-        // sortorder should be blank initially
-        $this->assertEmpty($category1->sortorder);
-
-        // Calling fix_course_sortorder() should provide a new sortorder
-        fix_course_sortorder();
-        $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
-
-        $this->assertGreaterThanOrEqual(1, $category1->sortorder);
-
-        // Create two more categories and test the sortorder worked correctly
-        $data->name = 'ccc';
-        $category2 = create_course_category($data);
-        $this->assertEmpty($category2->sortorder);
-
-        $data->name = 'bbb';
-        $category3 = create_course_category($data);
-        $this->assertEmpty($category3->sortorder);
-
-        // Calling fix_course_sortorder() should provide a new sortorder to give category1,
-        // category2, category3. New course categories are ordered by id not name
-        fix_course_sortorder();
-
-        $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
-        $category2 = $DB->get_record('course_categories', array('id' => $category2->id));
-        $category3 = $DB->get_record('course_categories', array('id' => $category3->id));
-
-        $this->assertGreaterThanOrEqual($category1->sortorder, $category2->sortorder);
-        $this->assertGreaterThanOrEqual($category2->sortorder, $category3->sortorder);
-        $this->assertGreaterThanOrEqual($category1->sortorder, $category3->sortorder);
-    }
-
     public function test_move_module_in_course() {
         global $DB;
 
@@ -1072,4 +1024,92 @@ class courselib_testcase extends advanced_testcase {
         $pagetypelist = course_page_type_list($pagetype, null, null);
         $this->assertEquals($pagetypelist, $testpagetypelist1);
     }
+
+    public function test_compare_activities_by_time_desc() {
+
+        // Let's create some test data.
+        $activitiesivities = array();
+        $x = new stdClass();
+        $x->timestamp = null;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 1;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 3;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 0;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 5;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 5;
+        $activities[] = $x;
+
+        // Do the sorting.
+        usort($activities, 'compare_activities_by_time_desc');
+
+        // Let's check the result.
+        $last = 10;
+        foreach($activities as $activity) {
+            if (empty($activity->timestamp)) {
+                $activity->timestamp = 0;
+            }
+            $this->assertLessThanOrEqual($last, $activity->timestamp);
+        }
+    }
+
+    public function test_compare_activities_by_time_asc() {
+
+        // Let's create some test data.
+        $activities = array();
+        $x = new stdClass();
+        $x->timestamp = null;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 1;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 3;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 0;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 5;
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $activities[] = $x;
+
+        $x = new stdClass();
+        $x->timestamp = 5;
+        $activities[] = $x;
+
+        // Do the sorting.
+        usort($activities, 'compare_activities_by_time_asc');
+
+        // Let's check the result.
+        $last = 0;
+        foreach($activities as $activity) {
+            if (empty($activity->timestamp)) {
+                $activity->timestamp = 0;
+            }
+            $this->assertGreaterThanOrEqual($last, $activity->timestamp);
+        }
+    }
 }
index 8560eef..d3a9811 100644 (file)
@@ -516,7 +516,8 @@ YUI.add('moodle-course-toolboxes', function(Y) {
                 .setAttrs({
                     'name'  : 'title',
                     'value' : titletext,
-                    'autocomplete' : 'off'
+                    'autocomplete' : 'off',
+                    'aria-describedby' : 'id_editinstructions'
                 })
                 .addClass('titleeditor');
             var editform = Y.Node.create('<form />')
@@ -524,6 +525,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
                 .setAttribute('action', '#');
             var editinstructions = Y.Node.create('<span />')
                 .addClass('editinstructions')
+                .setAttrs({'id' : 'id_editinstructions'})
                 .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
             var activityicon = element.one('img.activityicon').cloneNode();
 
index 4682f2e..0a8d906 100644 (file)
@@ -108,12 +108,7 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configtext('enrol_database/newcoursecategory', get_string('newcoursecategory', 'enrol_database'), '', ''));
 
     if (!during_initial_install()) {
-        require_once($CFG->dirroot.'/course/lib.php');
-        $options = array();
-        $parentlist = array();
-        make_categories_list($options, $parentlist);
-        $settings->add(new admin_setting_configselect('enrol_database/defaultcategory', get_string('defaultcategory', 'enrol_database'), get_string('defaultcategory_desc', 'enrol_database'), 1, $options));
-        unset($parentlist);
+        $settings->add(new admin_setting_configselect('enrol_database/defaultcategory', get_string('defaultcategory', 'enrol_database'), get_string('defaultcategory_desc', 'enrol_database'), 1, make_categories_options()));
     }
 
     $settings->add(new admin_setting_configtext('enrol_database/templatecourse', get_string('templatecourse', 'enrol_database'), get_string('templatecourse_desc', 'enrol_database'), ''));
index 7bb8894..c27ea27 100644 (file)
@@ -94,10 +94,7 @@ if ($ADMIN->fulltree) {
         $options = $yesno;
         $settings->add(new admin_setting_configselect('enrol_ldap/autocreate', get_string('autocreate_key', 'enrol_ldap'), get_string('autocreate', 'enrol_ldap'), 0, $options));
         if (!during_initial_install()) {
-            require_once($CFG->dirroot.'/course/lib.php');
-            $parentlist = array();
-            $options = array();
-            make_categories_list($options, $parentlist);
+            $options = make_categories_options();
             $settings->add(new admin_setting_configselect('enrol_ldap/category', get_string('category_key', 'enrol_ldap'), get_string('category', 'enrol_ldap'), key($options), $options));
         }
         $settings->add(new admin_setting_configtext_trim_lower('enrol_ldap/template', get_string('template_key', 'enrol_ldap'), get_string('template', 'enrol_ldap'), ''));
diff --git a/filter/glossary/yui/autolinker/autolinker.js b/filter/glossary/yui/autolinker/autolinker.js
deleted file mode 100644 (file)
index cb4129c..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-YUI.add('moodle-filter_glossary-autolinker', function(Y) {
-
-    var AUTOLINKERNAME = 'Glossary filter autolinker',
-        URL = 'url',
-        POPUPNAME = 'name',
-        POPUPOPTIONS = 'options',
-        TITLE = 'title',
-        WIDTH = 'width',
-        HEIGHT = 'height',
-        MENUBAR = 'menubar',
-        LOCATION = 'location',
-        SCROLLBARS = 'scrollbars',
-        RESIZEABLE = 'resizable',
-        TOOLBAR = 'toolbar',
-        STATUS = 'status',
-        DIRECTORIES = 'directories',
-        FULLSCREEN = 'fullscreen',
-        DEPENDENT = 'dependent';
-
-    var AUTOLINKER = function() {
-        AUTOLINKER.superclass.constructor.apply(this, arguments);
-    };
-    Y.extend(AUTOLINKER, Y.Base, {
-        overlay : null,
-        initializer : function(config) {
-            var popupname = this.get(POPUPNAME),
-                popupoptions = this.get(POPUPOPTIONS),
-                self = this;
-            Y.delegate('click', function(e){
-
-                e.preventDefault();
-
-                //display a progress indicator
-                var title = '';
-                var content = Y.Node.create('<div id="glossaryfilteroverlayprogress"><img src="'+M.cfg.loadingicon+'" class="spinner" /></div>');
-                var o = new Y.Overlay({
-                    headerContent :  title,
-                    bodyContent : content
-                });
-                self.overlay = o;
-                o.render(Y.one(document.body));
-
-                //Switch over to the ajax url and fetch the glossary item
-                var fullurl = this.getAttribute('href').replace('showentry.php','showentry_ajax.php');
-                var cfg = {
-                    method: 'get',
-                    context : self,
-                    on: {
-                        success: function(id, o, node) {
-                            this.display_callback(o.responseText);
-                        },
-                        failure: function(id, o, node) {
-                            var debuginfo = o.statusText;
-                            if (M.cfg.developerdebug) {
-                                o.statusText += ' (' + fullurl + ')';
-                            }
-                            this.display_callback('bodyContent',debuginfo);
-                        }
-                    }
-                };
-                Y.io(fullurl, cfg);
-
-            }, Y.one(document.body), 'a.glossary.autolink.concept');
-        },
-        display_callback : function(content) {
-            try {
-                var data = Y.JSON.parse(content);
-                if (data.success){
-                    this.overlay.hide(); //hide progress indicator
-
-                    for (key in data.entries) {
-                        definition = data.entries[key].definition + data.entries[key].attachments
-                        var alertpanel = new M.core.alert({title:data.entries[key].concept, message:definition, lightbox:false});
-                        Y.Node.one('#id_yuialertconfirm-' + alertpanel.COUNT).focus();
-                    }
-
-                    return true;
-                } else if (data.error) {
-                    new M.core.ajaxException(data);
-                }
-            }catch(e) {
-                new M.core.exception(e);
-            }
-            return false;
-        }
-    }, {
-        NAME : AUTOLINKERNAME,
-        ATTRS : {
-            url : {
-                validator : Y.Lang.isString,
-                value : M.cfg.wwwroot+'/mod/glossary/showentry.php'
-            },
-            name : {
-                validator : Y.Lang.isString,
-                value : 'glossaryconcept'
-            },
-            options : {
-                getter : function(val) {
-                    return {
-                        width : this.get(WIDTH),
-                        height : this.get(HEIGHT),
-                        menubar : this.get(MENUBAR),
-                        location : this.get(LOCATION),
-                        scrollbars : this.get(SCROLLBARS),
-                        resizable : this.get(RESIZEABLE),
-                        toolbar : this.get(TOOLBAR),
-                        status : this.get(STATUS),
-                        directories : this.get(DIRECTORIES),
-                        fullscreen : this.get(FULLSCREEN),
-                        dependent : this.get(DEPENDENT)
-                    }
-                },
-                readOnly : true
-            },
-            width : {value : 600},
-            height : {value : 450},
-            menubar : {value : false},
-            location : {value : false},
-            scrollbars : {value : true},
-            resizable : {value : true},
-            toolbar : {value : true},
-            status : {value : true},
-            directories : {value : false},
-            fullscreen : {value : false},
-            dependent : {value : true}
-        }
-    });
-
-    M.filter_glossary = M.filter_glossary || {};
-    M.filter_glossary.init_filter_autolinking = function(config) {
-        return new AUTOLINKER(config);
-    }
-
-}, '@VERSION@', {requires:['base','node','io-base','json-parse','event-delegate','overlay','moodle-core-notification']});
diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-coverage.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-coverage.js
new file mode 100644 (file)
index 0000000..320c8c0
Binary files /dev/null and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-coverage.js differ
diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js
new file mode 100644 (file)
index 0000000..467c6a3
Binary files /dev/null and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js differ
diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js
new file mode 100644 (file)
index 0000000..303c17d
Binary files /dev/null and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js differ
diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js
new file mode 100644 (file)
index 0000000..467c6a3
Binary files /dev/null and b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js differ
diff --git a/filter/glossary/yui/src/autolinker/build.json b/filter/glossary/yui/src/autolinker/build.json
new file mode 100644 (file)
index 0000000..e6e0e90
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "name": "moodle-filter_glossary-autolinker",
+  "builds": {
+    "moodle-filter_glossary-autolinker": {
+      "jsfiles": [
+        "autolinker.js"
+      ]
+    }
+  }
+}
diff --git a/filter/glossary/yui/src/autolinker/js/autolinker.js b/filter/glossary/yui/src/autolinker/js/autolinker.js
new file mode 100644 (file)
index 0000000..8d552cc
--- /dev/null
@@ -0,0 +1,129 @@
+var AUTOLINKERNAME = 'Glossary filter autolinker',
+    WIDTH = 'width',
+    HEIGHT = 'height',
+    MENUBAR = 'menubar',
+    LOCATION = 'location',
+    SCROLLBARS = 'scrollbars',
+    RESIZEABLE = 'resizable',
+    TOOLBAR = 'toolbar',
+    STATUS = 'status',
+    DIRECTORIES = 'directories',
+    FULLSCREEN = 'fullscreen',
+    DEPENDENT = 'dependent',
+    AUTOLINKER;
+
+AUTOLINKER = function() {
+    AUTOLINKER.superclass.constructor.apply(this, arguments);
+};
+Y.extend(AUTOLINKER, Y.Base, {
+    overlay : null,
+    initializer : function() {
+        var self = this;
+        Y.delegate('click', function(e){
+            e.preventDefault();
+
+            //display a progress indicator
+            var title = '',
+                content = Y.Node.create('<div id="glossaryfilteroverlayprogress"><img src="'+M.cfg.loadingicon+'" class="spinner" /></div>'),
+                o = new Y.Overlay({
+                    headerContent :  title,
+                    bodyContent : content
+                }),
+                fullurl,
+                cfg;
+            self.overlay = o;
+            o.render(Y.one(document.body));
+
+            //Switch over to the ajax url and fetch the glossary item
+            fullurl = this.getAttribute('href').replace('showentry.php','showentry_ajax.php');
+            cfg = {
+                method: 'get',
+                context : self,
+                on: {
+                    success: function(id, o) {
+                        this.display_callback(o.responseText);
+                    },
+                    failure: function(id, o) {
+                        var debuginfo = o.statusText;
+                        if (M.cfg.developerdebug) {
+                            o.statusText += ' (' + fullurl + ')';
+                        }
+                        this.display_callback('bodyContent',debuginfo);
+                    }
+                }
+            };
+            Y.io(fullurl, cfg);
+
+        }, Y.one(document.body), 'a.glossary.autolink.concept');
+    },
+    display_callback : function(content) {
+        var data,
+            key,
+            alertpanel;
+        try {
+            data = Y.JSON.parse(content);
+            if (data.success){
+                this.overlay.hide(); //hide progress indicator
+
+                for (key in data.entries) {
+                    definition = data.entries[key].definition + data.entries[key].attachments;
+                    alertpanel = new M.core.alert({title:data.entries[key].concept, message:definition, lightbox:false});
+                    Y.Node.one('#id_yuialertconfirm-' + alertpanel.COUNT).focus();
+                }
+
+                return true;
+            } else if (data.error) {
+                new M.core.ajaxException(data);
+            }
+        } catch(e) {
+            new M.core.exception(e);
+        }
+        return false;
+    }
+}, {
+    NAME : AUTOLINKERNAME,
+    ATTRS : {
+        url : {
+            validator : Y.Lang.isString,
+            value : M.cfg.wwwroot+'/mod/glossary/showentry.php'
+        },
+        name : {
+            validator : Y.Lang.isString,
+            value : 'glossaryconcept'
+        },
+        options : {
+            getter : function() {
+                return {
+                    width : this.get(WIDTH),
+                    height : this.get(HEIGHT),
+                    menubar : this.get(MENUBAR),
+                    location : this.get(LOCATION),
+                    scrollbars : this.get(SCROLLBARS),
+                    resizable : this.get(RESIZEABLE),
+                    toolbar : this.get(TOOLBAR),
+                    status : this.get(STATUS),
+                    directories : this.get(DIRECTORIES),
+                    fullscreen : this.get(FULLSCREEN),
+                    dependent : this.get(DEPENDENT)
+                };
+            },
+            readOnly : true
+        },
+        width : {value : 600},
+        height : {value : 450},
+        menubar : {value : false},
+        location : {value : false},
+        scrollbars : {value : true},
+        resizable : {value : true},
+        toolbar : {value : true},
+        status : {value : true},
+        directories : {value : false},
+        fullscreen : {value : false},
+        dependent : {value : true}
+    }
+});
+
+M.filter_glossary = M.filter_glossary || {};
+M.filter_glossary.init_filter_autolinking = function(config) {
+    return new AUTOLINKER(config);
+};
diff --git a/filter/glossary/yui/src/autolinker/meta/autolinker.json b/filter/glossary/yui/src/autolinker/meta/autolinker.json
new file mode 100644 (file)
index 0000000..f97fb16
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "moodle-filter_glossary-autolinker": {
+    "requires": [
+        "base",
+        "node",
+        "io-base",
+        "json-parse",
+        "event-delegate",
+        "overlay",
+        "moodle-core-notification"
+    ]
+  }
+}
diff --git a/grade/externallib.php b/grade/externallib.php
new file mode 100644 (file)
index 0000000..a39aceb
--- /dev/null
@@ -0,0 +1,293 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * External assign API
+ *
+ * @package    core_grade
+ * @since      Moodle 2.5
+ * @copyright  2013 Paul Charsley
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/externallib.php");
+require_once("$CFG->dirroot/grade/grading/lib.php");
+
+/**
+ * core grade functions
+ */
+class core_grade_external extends external_api {
+
+    /**
+     * Describes the parameters for get_definitions
+     * @return external_function_parameters
+     * @since Moodle 2.5
+     */
+    public static function get_definitions_parameters () {
+        return new external_function_parameters(
+            array(
+                'cmids' => new external_multiple_structure(
+                    new external_value(PARAM_INT, 'course module id'), '1 or more course module ids'),
+                'areaname' => new external_value(PARAM_AREA, 'area name'),
+                'activeonly' => new external_value(PARAM_BOOL, 'Only the active method', VALUE_DEFAULT, 0)
+            )
+        );
+    }
+
+    /**
+     * Returns the definitions for the requested course module ids
+     * @param array of ints $cmids
+     * @param string $areaname
+     * @param boolean $activeonly default is false, if true, only the active method is returned
+     * @return array of areas with definitions for each requested course module id
+     * @since Moodle 2.5
+     */
+    public static function get_definitions ($cmids, $areaname, $activeonly = false) {
+        global $DB, $CFG;
+        require_once("$CFG->dirroot/grade/grading/form/lib.php");
+        $params = self::validate_parameters(self::get_definitions_parameters(),
+                      array('cmids' => $cmids,
+                            'areaname' => $areaname,
+                            'activeonly' => $activeonly));
+        $warnings = array();
+        $areas = array();
+        foreach ($params['cmids'] as $cmid) {
+            $context = context_module::instance($cmid);
+            try {
+                self::validate_context($context);
+            } catch (Exception $e) {
+                $warnings[] = array(
+                    'item' => 'module',
+                    'itemid' => $cmid,
+                    'message' => 'No access rights in module context',
+                    'warningcode' => '1'
+                );
+                continue;
+            }
+            // Check if the user has managegradingforms capability.
+            $isgradingmethodmanager = false;
+            if (has_capability('moodle/grade:managegradingforms', $context)) {
+                $isgradingmethodmanager = true;
+            }
+            $module = get_coursemodule_from_id('', $cmid, 0, false, MUST_EXIST);
+            $componentname = "mod_".$module->modname;
+
+            // Get the grading manager.
+            $gradingmanager = get_grading_manager($context, $componentname, $params['areaname']);
+            // Get the controller for each grading method.
+            $methods = array();
+            if ($params['activeonly'] == true) {
+                $methods[] = $gradingmanager->get_active_method();
+            } else {
+                $methods = array_keys($gradingmanager->get_available_methods(false));
+            }
+
+            $area = array();
+            $area['cmid'] = $cmid;
+            $area['contextid'] = $context->id;
+            $area['component'] = $componentname;
+            $area['activemethod'] = $gradingmanager->get_active_method();
+            $area['definitions'] = array();
+
+            foreach ($methods as $method) {
+                $controller = $gradingmanager->get_controller($method);
+                $def = $controller->get_definition(true);
+                if ($def == false) {
+                    continue;
+                }
+                if ($isgradingmethodmanager == false) {
+                    $isviewable = true;
+                    if ($def->status != gradingform_controller::DEFINITION_STATUS_READY) {
+                        $warnings[] = array(
+                            'item' => 'module',
+                            'itemid' => $cmid,
+                            'message' => 'Capability moodle/grade:managegradingforms required to view draft definitions',
+                            'warningcode' => '1'
+                        );
+                        $isviewable = false;
+                    }
+                    if (!empty($def->options)) {
+                        $options = json_decode($def->options);
+                        if (isset($options->alwaysshowdefinition) &&
+                                $options->alwaysshowdefinition == 0) {
+                            $warnings[] = array(
+                                'item' => 'module',
+                                'itemid' => $cmid,
+                                'message' => 'Capability moodle/grade:managegradingforms required to preview definition',
+                                'warningcode' => '1'
+                            );
+                            $isviewable = false;
+                        }
+                    }
+                    if ($isviewable == false) {
+                        continue;
+                    }
+                }
+                $definition = array();
+                $definition['id'] = $def->id;
+                $definition['method'] = $method;
+                $definition['name'] = $def->name;
+                $definition['description'] = $def->description;
+                $definition['descriptionformat'] = $def->descriptionformat;
+                $definition['status'] = $def->status;
+                $definition['copiedfromid'] = $def->copiedfromid;
+                $definition['timecreated'] = $def->timecreated;
+                $definition['usercreated'] = $def->usercreated;
+                $definition['timemodified'] = $def->timemodified;
+                $definition['usermodified'] = $def->usermodified;
+                $definition['timecopied'] = $def->timecopied;
+                // Format the description text field.
+                $formattedtext = external_format_text($definition['description'],
+                                                      $definition['descriptionformat'],
+                                                      $context->id,
+                                                      $componentname,
+                                                      'description',
+                                                      $def->id);
+                $definition['description'] = $formattedtext[0];
+                $definition['descriptionformat'] = $formattedtext[1];
+
+                $details = $controller->get_external_definition_details();
+                $items = array();
+                foreach ($details as $key => $value) {
+                    $items[$key] = self::format_text($def->{$key}, $context->id, $componentname, $def->id);
+                }
+                $definition[$method] = $items;
+                $area['definitions'][] = $definition;
+            }
+            $areas[] = $area;
+        }
+        $result = array(
+            'areas' => $areas,
+            'warnings' => $warnings
+        );
+        return $result;
+    }
+
+    /**
+     * Recursively processes all elements in an array and runs external_format_text()on
+     * all elements which have a text field and associated format field with a key name
+     * that ends with the text 'format'. The modified array is returned.
+     * @param array $items the array to be processed
+     * @param int $contextid
+     * @param string $componentname
+     * @param int $itemid
+     * @see external_format_text in lib/externallib.php
+     * @return array the input array with all fields formatted
+     */
+    private static function format_text($items, $contextid, $componentname, $itemid) {
+        $formatkeys = array();
+        foreach ($items as $key => $value) {
+            if (!is_array($value) && substr_compare($key, 'format', -6, 6) === 0) {
+                $formatkeys[] = $key;
+            }
+        }
+        foreach ($formatkeys as $formatkey) {
+            $descriptionkey = substr($formatkey, 0, -6);
+            $formattedtext = external_format_text($items[$descriptionkey],
+                                                  $items[$formatkey],
+                                                  $contextid,
+                                                  $componentname,
+                                                  'description',
+                                                  $itemid);
+            $items[$descriptionkey] = $formattedtext[0];
+            $items[$formatkey] = $formattedtext[1];
+        }
+        foreach ($items as $key => $value) {
+            if (is_array($value)) {
+                $items[$key] = self::format_text($value, $contextid, $componentname, $itemid);
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * Creates a grading area
+     * @return external_single_structure
+     * @since  Moodle 2.5
+     */
+    private static function grading_area() {
+        return new external_single_structure(
+            array (
+                'cmid'    => new external_value(PARAM_INT, 'course module id'),
+                'contextid'  => new external_value(PARAM_INT, 'context id'),
+                'component' => new external_value(PARAM_TEXT, 'component name'),
+                'activemethod' => new external_value(PARAM_TEXT, 'active method', VALUE_OPTIONAL),
+                'definitions'  => new external_multiple_structure(self::definition(), 'definitions')
+            )
+        );
+    }
+
+    /**
+     * creates a grading form definition
+     * @return external_single_structure
+     * @since  Moodle 2.5
+     */
+    private static function definition() {
+        global $CFG;
+        $definition = array();
+        $definition['id']                = new external_value(PARAM_INT, 'definition id');
+        $definition['method']            = new external_value(PARAM_TEXT, 'method');
+        $definition['name']              = new external_value(PARAM_TEXT, 'name');
+        $definition['description']       = new external_value(PARAM_RAW, 'description');
+        $definition['descriptionformat'] = new external_format_value('description');
+        $definition['status']            = new external_value(PARAM_INT, 'status');
+        $definition['copiedfromid']      = new external_value(PARAM_INT, 'copied from id', VALUE_OPTIONAL);
+        $definition['timecreated']       = new external_value(PARAM_INT, 'creation time');
+        $definition['usercreated']       = new external_value(PARAM_INT, 'user who created definition');
+        $definition['timemodified']      = new external_value(PARAM_INT, 'last modified time');
+        $definition['usermodified']      = new external_value(PARAM_INT, 'user who modified definition');
+        $definition['timecopied']        = new external_value(PARAM_INT, 'time copied', VALUE_OPTIONAL);
+        foreach (self::get_grading_methods() as $method) {
+            require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
+            $details  = call_user_func('gradingform_'.$method.'_controller::get_external_definition_details');
+            if ($details != null) {
+                $items = array();
+                foreach ($details as $key => $value) {
+                    $details[$key]->required = VALUE_OPTIONAL;
+                    $items[$key] = $value;
+                }
+                $definition[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
+            }
+        }
+        return new external_single_structure($definition);
+    }
+
+    /**
+     * Describes the get_definitions return value
+     * @return external_single_structure
+     * @since Moodle 2.5
+     */
+    public static function get_definitions_returns() {
+        return new external_single_structure(
+            array(
+                'areas' => new external_multiple_structure(self::grading_area(), 'list of grading areas'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
+    /**
+     * @return array of available grading methods
+     * @since Moodle 2.5
+     */
+    private static function get_grading_methods() {
+        $methods = array_keys(grading_manager::available_methods(false));
+        return $methods;
+    }
+
+}
index 15ef0d7..b6b9ec2 100644 (file)
@@ -643,6 +643,40 @@ class gradingform_guide_controller extends gradingform_controller {
         }
         return $returnvalue;
     }
+
+    /**
+     * @return array An array containing 2 key/value pairs which hold the external_multiple_structure
+     * for the 'guide_criteria' and the 'guide_comment'.
+     * @see gradingform_controller::get_external_definition_details()
+     * @since Moodle 2.5
+     */
+    public static function get_external_definition_details() {
+        $guide_criteria = new external_multiple_structure(
+                              new external_single_structure(
+                                  array(
+                                      'id'   => new external_value(PARAM_INT, 'criterion id'),
+                                      'sortorder' => new external_value(PARAM_INT, 'sortorder'),
+                                      'description' => new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL),
+                                      'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
+                                      'shortname' => new external_value(PARAM_TEXT, 'description'),
+                                      'descriptionmarkers' => new external_value(PARAM_RAW, 'markers description', VALUE_OPTIONAL),
+                                      'descriptionmarkersformat' => new external_format_value('descriptionmarkers', VALUE_OPTIONAL),
+                                      'maxscore' => new external_value(PARAM_FLOAT, 'maximum score')
+                                      )
+                                  )
+        );
+        $guide_comment = new external_multiple_structure(
+                              new external_single_structure(
+                                  array(
+                                      'id'   => new external_value(PARAM_INT, 'criterion id'),
+                                      'sortorder' => new external_value(PARAM_INT, 'sortorder'),
+                                      'description' => new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL),
+                                      'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL)
+                                   )
+                              ), 'comments', VALUE_OPTIONAL
+        );
+        return array('guide_criteria' => $guide_criteria, 'guide_comment' => $guide_comment);
+    }
 }
 
 /**
index c096870..6c8c8ac 100644 (file)
@@ -642,6 +642,22 @@ abstract class gradingform_controller {
         }
         return $this->graderange;
     }
+
+    /**
+     * Overridden by sub classes that wish to make definition details available to web services.
+     * When not overridden, only definition data common to all grading methods is made available.
+     * When overriding, the return value should be an array containing one or more key/value pairs.
+     * These key/value pairs should match the definition returned by the get_definition() function.
+     * For examples, look at:
+     *    $gradingform_rubric_controller->get_external_definition_details()
+     *    $gradingform_guide_controller->get_external_definition_details()
+     * @return array An array of one or more key/value pairs containing the external_multiple_structure/s
+     * corresponding to the definition returned by $controller->get_definition()
+     * @since Moodle 2.5
+     */
+    public static function get_external_definition_details() {
+        return null;
+    }
 }
 
 /**
index 22ce4c9..7f08a4e 100644 (file)
@@ -654,6 +654,36 @@ class gradingform_rubric_controller extends gradingform_controller {
         }
         return $returnvalue;
     }
+
+    /**
+     * @return array An array containing a single key/value pair with the 'rubric_criteria' external_multiple_structure.
+     * @see gradingform_controller::get_external_definition_details()
+     * @since Moodle 2.5
+     */
+    public static function get_external_definition_details() {
+        $rubric_criteria = new external_multiple_structure(
+            new external_single_structure(
+                array(
+                   'id'   => new external_value(PARAM_INT, 'criterion id'),
+                   'sortorder' => new external_value(PARAM_INT, 'sortorder'),
+                   'description' => new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL),
+                   'descriptionformat' => new external_format_value('description', VALUE_OPTIONAL),
+                   'levels' => new external_multiple_structure(
+                                   new external_single_structure(
+                                       array(
+                                        'id' => new external_value(PARAM_INT, 'level id'),
+                                        'score' => new external_value(PARAM_FLOAT, 'score'),
+                                        'definition' => new external_value(PARAM_RAW, 'definition', VALUE_OPTIONAL),
+                                        'definitionformat' => new external_format_value('definition', VALUE_OPTIONAL)
+                                       )
+                                  ), 'levels', VALUE_OPTIONAL
+                              )
+                   )
+              ), 'definition details', VALUE_OPTIONAL
+        );
+        return array('rubric_criteria' => $rubric_criteria);
+    }
+
 }
 
 /**
diff --git a/grade/tests/externallib_test.php b/grade/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..a1cbfc5
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+
+/**
+ * External core grade functions unit tests
+ *
+ * @package core_grade
+ * @category external
+ * @copyright 2013 Paul Charsley
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_grade_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Tests set up
+     */
+    protected function setUp() {
+        global $CFG;
+        require_once($CFG->dirroot . '/grade/externallib.php');
+    }
+
+    /**
+     * Test get_definitions
+     */
+    public function test_get_definitions () {
+        global $DB, $CFG, $USER;
+
+        $this->resetAfterTest(true);
+        // Create a course and assignment.
+        $coursedata['idnumber'] = 'idnumbercourse';
+        $coursedata['fullname'] = 'Lightwork Course';
+        $coursedata['summary'] = 'Lightwork Course description';
+        $coursedata['summaryformat'] = FORMAT_MOODLE;
+        $course = self::getDataGenerator()->create_course($coursedata);
+
+        $assigndata['course'] = $course->id;
+        $assigndata['name'] = 'lightwork assignment';
+
+        $cm = self::getDataGenerator()->create_module('assign', $assigndata);
+
+        // Create manual enrolment record.
+        $manual_enrol_data['enrol'] = 'manual';
+        $manual_enrol_data['status'] = 0;
+        $manual_enrol_data['courseid'] = $course->id;
+        $enrolid = $DB->insert_record('enrol', $manual_enrol_data);
+
+        // Create a teacher and give them capabilities.
+        $coursecontext = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $coursecontext->id, 3);
+        $modulecontext = context_module::instance($cm->id);
+        $this->assignUserCapability('mod/assign:grade', $modulecontext->id, $roleid);
+
+        // Create the teacher's enrolment record.
+        $user_enrolment_data['status'] = 0;
+        $user_enrolment_data['enrolid'] = $enrolid;
+        $user_enrolment_data['userid'] = $USER->id;
+        $DB->insert_record('user_enrolments', $user_enrolment_data);
+
+        // Create a grading area.
+        $gradingarea = array(
+            'contextid' => $modulecontext->id,
+            'component' => 'mod_assign',
+            'areaname' => 'submissions',
+            'activemethod' => 'rubric'
+        );
+        $areaid = $DB->insert_record('grading_areas', $gradingarea);
+
+        // Create a rubric grading definition.
+        $rubricdefinition = array (
+            'areaid' => $areaid,
+            'method' => 'rubric',
+            'name' => 'test',
+            'status' => 20,
+            'copiedfromid' => 1,
+            'timecreated' => 1,
+            'usercreated' => $USER->id,
+            'timemodified' => 1,
+            'usermodified' => $USER->id,
+            'timecopied' => 0
+        );
+        $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
+
+        // Create a criterion with levels.
+        $rubriccriteria1 = array (
+            'definitionid' => $definitionid,
+            'sortorder' => 1,
+            'description' => 'Demonstrate an understanding of disease control',
+            'descriptionformat' => 0
+        );
+        $criterionid1 = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria1);
+        $rubriclevel1 = array (
+            'criterionid' => $criterionid1,
+            'score' => 5,
+            'definition' => 'pass',
+            'definitionformat' => 0
+        );
+        $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
+        $rubriclevel2 = array (
+            'criterionid' => $criterionid1,
+            'score' => 10,
+            'definition' => 'excellent',
+            'definitionformat' => 0
+        );
+        $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
+
+        // Create a second criterion with levels.
+        $rubriccriteria2 = array (
+            'definitionid' => $definitionid,
+            'sortorder' => 2,
+            'description' => 'Demonstrate an understanding of brucellosis',
+            'descriptionformat' => 0
+        );
+        $criterionid2 = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria2);
+        $rubriclevel1 = array (
+            'criterionid' => $criterionid2,
+            'score' => 5,
+            'definition' => 'pass',
+            'definitionformat' => 0
+        );
+        $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
+        $rubriclevel2 = array (
+            'criterionid' => $criterionid2,
+            'score' => 10,
+            'definition' => 'excellent',
+            'definitionformat' => 0
+        );
+        $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
+
+        // Call the external function.
+        $cmids = array ($cm->id);
+        $areaname = 'submissions';
+        $result = core_grade_external::get_definitions($cmids, $areaname);
+
+        $this->assertEquals(1, count($result['areas']));
+        $this->assertEquals(1, count($result['areas'][0]['definitions']));
+        $definition = $result['areas'][0]['definitions'][0];
+
+        $this->assertEquals($rubricdefinition['method'], $definition['method']);
+        $this->assertEquals($USER->id, $definition['usercreated']);
+
+        require_once("$CFG->dirroot/grade/grading/lib.php");
+        require_once($CFG->dirroot.'/grade/grading/form/'.$rubricdefinition['method'].'/lib.php');
+
+        $gradingmanager = get_grading_manager($areaid);
+
+        $this->assertEquals(1, count($definition[$rubricdefinition['method']]));
+
+        $rubricdetails = $definition[$rubricdefinition['method']];
+        $details = call_user_func('gradingform_'.$rubricdefinition['method'].'_controller::get_external_definition_details');
+
+        $this->assertEquals(2, count($rubricdetails[key($details)]));
+
+        $found = false;
+        foreach ($rubricdetails[key($details)] as $criterion) {
+            if ($criterion['id'] == $criterionid1) {
+                $this->assertEquals($rubriccriteria1['description'], $criterion['description']);
+                $this->assertEquals(2, count($criterion['levels']));
+                $found = true;
+                break;
+            }
+        }
+        $this->assertTrue($found);
+    }
+
+}
index 98f2c9d..46f0137 100644 (file)
@@ -67,13 +67,11 @@ class group_form extends moodleform {
         $mform->addHelpButton('enrolmentkey', 'enrolmentkey', 'group');
         $mform->setType('enrolmentkey', PARAM_RAW);
 
-        if (!empty($CFG->gdversion)) {
-            $options = array(get_string('no'), get_string('yes'));
-            $mform->addElement('select', 'hidepicture', get_string('hidepicture'), $options);
+        $options = array(get_string('no'), get_string('yes'));
+        $mform->addElement('select', 'hidepicture', get_string('hidepicture'), $options);
 
-            $mform->addElement('filepicker', 'imagefile', get_string('newpicture', 'group'));
-            $mform->addHelpButton('imagefile', 'newpicture', 'group');
-        }
+        $mform->addElement('filepicker', 'imagefile', get_string('newpicture', 'group'));
+        $mform->addHelpButton('imagefile', 'newpicture', 'group');
 
         $mform->addElement('hidden','id');
         $mform->setType('id', PARAM_INT);
index bd0da5c..b8d13b0 100644 (file)
@@ -324,20 +324,18 @@ function groups_update_group_icon($group, $data, $editform) {
     $context = context_course::instance($group->courseid, MUST_EXIST);
 
     //TODO: it would make sense to allow picture deleting too (skodak)
-    if (!empty($CFG->gdversion)) {
-        if ($iconfile = $editform->save_temp_file('imagefile')) {
-            if (process_new_icon($context, 'group', 'icon', $group->id, $iconfile)) {
-                $DB->set_field('groups', 'picture', 1, array('id'=>$group->id));
-                $group->picture = 1;
-            } else {
-                $fs->delete_area_files($context->id, 'group', 'icon', $group->id);
-                $DB->set_field('groups', 'picture', 0, array('id'=>$group->id));
-                $group->picture = 0;
-            }
-            @unlink($iconfile);
-            // Invalidate the group data as we've updated the group record.
-            cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($group->courseid));
+    if ($iconfile = $editform->save_temp_file('imagefile')) {
+        if (process_new_icon($context, 'group', 'icon', $group->id, $iconfile)) {
+            $DB->set_field('groups', 'picture', 1, array('id'=>$group->id));
+            $group->picture = 1;
+        } else {
+            $fs->delete_area_files($context->id, 'group', 'icon', $group->id);
+            $DB->set_field('groups', 'picture', 0, array('id'=>$group->id));
+            $group->picture = 0;
         }
+        @unlink($iconfile);
+        // Invalidate the group data as we've updated the group record.
+        cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($group->courseid));
     }
 }
 
index 9bfba65..4d1bdc4 100644 (file)
@@ -224,7 +224,6 @@ $string['configforceloginforprofiles'] = 'This setting forces people to login as
 $string['configfrontpage'] = 'The items selected above will be displayed on the site\'s front page.';
 $string['configfrontpageloggedin'] = 'The items selected above will be displayed on the site\'s front page when a user is logged in.';
 $string['configfullnamedisplay'] = 'This defines how names are shown when they are displayed in full. For most mono-lingual sites the most efficient setting is "First name + Surname", but you may choose to hide surnames altogether, or to leave it up to the current language pack to decide (some languages have different conventions).';
-$string['configgdversion'] = 'Indicate the version of GD that is installed.  The version shown by default is the one that has been auto-detected.  Don\'t change this unless you really know what you\'re doing.';
 $string['configgeoipfile'] = 'Location of GeoIP City binary data file. This file is not part of Moodle distribution and must be obtained separately from <a href="http://www.maxmind.com/">MaxMind</a>. You can either buy a commercial version or use the free version.<br />Simply download <a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz" >http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz</a> and extract it into "{$a}" directory on your server.';
 $string['configgetremoteaddrconf'] = 'If your server is behind a reverse proxy, you can use this setting to specify which HTTP headers can be trusted to contain the remote IP address. The headers are read in order, using the first one that is available.';
 $string['configgradebookroles'] = 'This setting allows you to control who appears on the gradebook.  Users need to have at least one of these roles in a course to be shown in the gradebook for that course.';
@@ -546,7 +545,7 @@ $string['frontpageroles'] = 'Front page roles';
 $string['frontpagesettings'] = 'Front page settings';
 $string['fullnamedisplay'] = 'Full name format';
 $string['gdrecommended'] = 'GD extension is used for conversion of images, some features such as user profile images will not be available if missing.';
-$string['gdversion'] = 'GD version';
+$string['gdrequired'] = 'The GD extension is now required by Moodle for image conversion.';
 $string['generalsettings'] = 'General settings';
 $string['geoipfile'] = 'GeoIP city data file';
 $string['getremoteaddrconf'] = 'Logged IP address source';
index aec3fe8..d081b61 100644 (file)
@@ -36,6 +36,9 @@ $string['cacheadmin'] = 'Cache administration';
 $string['cacheconfig'] = 'Configuration';
 $string['cachedef_calendar_subscriptions'] = 'Calendar subscriptions';
 $string['cachedef_config'] = 'Config settings';
+$string['cachedef_coursecat'] = 'Course categories lists for particular user';
+$string['cachedef_coursecatrecords'] = 'Course categories records';
+$string['cachedef_coursecattree'] = 'Course categories tree';
 $string['cachedef_databasemeta'] = 'Database meta information';
 $string['cachedef_eventinvalidation'] = 'Event invalidation';
 $string['cachedef_groupdata'] = 'Course group information';
index 81544cc..622b247 100644 (file)
@@ -105,6 +105,7 @@ $string['cannotmarktopic'] = 'Could not mark that topic for this course';
 $string['cannotmigratedatacomments'] = 'Cannot migrate data module comments';
 $string['cannotmodulename'] = 'Cannot get the module name in build navigation';
 $string['cannotmoduletype'] = 'Cannot get the module type in build navigation';
+$string['cannotmovecategory'] = 'Cannot move category';
 $string['cannotmoverolewithid'] = 'Cannot move role with ID {$a}';
 $string['cannotopencsv'] = 'Cannot open CSV file';
 $string['cannotopenfile'] = 'Cannot open file ({$a})';
@@ -159,7 +160,9 @@ $string['cannotviewprofile'] = 'You cannot view the profile of this user';
 $string['cannotviewreport'] = 'You cannot view this report';
 $string['cannotwritefile'] = 'Cannot write to file ({$a})';
 $string['categoryerror'] = 'Category error';
+$string['categorynamerequired'] = 'Category name is required';
 $string['categorytoolong'] = 'Category name too long';
+$string['categoryidnumbertaken'] = 'ID number is already used for another category';
 $string['commentmisconf'] = 'Comment ID is misconfigured';
 $string['componentisuptodate'] = 'Component is up-to-date';
 $string['confirmsesskeybad'] = 'Sorry, but your session key could not be confirmed to carry out this action.  This security feature prevents against accidental or malicious execution of important functions in your name.  Please make sure you really wanted to execute this function.';
index aceb61f..e936e0d 100644 (file)
@@ -226,18 +226,6 @@ $string['fileuploadshelp'] = '<p>File uploading seems to be disabled on your ser
 <p>To enable file uploading you (or your system administrator) will need to 
    edit the main php.ini file on your system and change the setting for 
    <b>file_uploads</b> to \'1\'.</p>';
-$string['gdversion'] = 'GD version';
-$string['gdversionerror'] = 'The GD library should be present to process and create images';
-$string['gdversionhelp'] = '<p>Your server does not seem to have GD installed.</p>
-
-<p>GD is a library that is required by PHP to allow Moodle to process images 
-   (such as the user profile icons) and to create new images (such as 
-   the log graphs).  Moodle will still work without GD - these features 
-   will just not be available to you.</p>
-
-<p>To add GD to PHP under Unix, compile PHP using the --with-gd parameter.</p>
-
-<p>Under Windows you can usually edit php.ini and uncomment the line referencing php_gd2.dll.</p>';
 $string['globalsquotes'] = 'Insecure handling of globals';
 $string['globalsquoteserror'] = 'Fix your PHP settings: disable register_globals and/or enable magic_quotes_gpc';
 $string['globalsquoteshelp'] = '<p>Combination of disabled magic quotes GPC and enabled register globals both at the same time is not recommended.</p>
index fe13216..a2248ed 100644 (file)
@@ -731,10 +731,6 @@ $string['fullnameuser'] = 'User full name';
 $string['fullprofile'] = 'Full profile';
 $string['fullsitename'] = 'Full site name';
 $string['functiondisabled'] = 'That functionality is currently disabled';
-$string['gdneed'] = 'GD must be installed to see this graph';
-$string['gdnot'] = 'GD is not installed';
-$string['gd1'] = 'GD 1.x is installed';
-$string['gd2'] = 'GD 2.x is installed';
 $string['general'] = 'General';
 $string['geolocation'] = 'latitude - longitude';
 $string['gettheselogs'] = 'Get these logs';
@@ -973,6 +969,7 @@ $string['lookback'] = 'Look back';
 $string['mailadmins'] = 'Inform admins';
 $string['mailstudents'] = 'Inform students';
 $string['mailteachers'] = 'Inform teachers';
+$string['maincoursepage'] = 'Main course page';
 $string['makeafolder'] = 'Create folder';
 $string['makeeditable'] = 'If you make \'{$a}\' editable by the web server process (eg apache) then you could edit this file directly from this page';
 $string['makethismyhome'] = 'Make this my default home page';
@@ -1419,7 +1416,6 @@ $string['restoreusersprecheck'] = 'Checking user data';
 $string['restoreusersprecheckerror'] = 'Some problems were detected when checking user data';
 $string['restricted'] = 'Restricted';
 $string['returningtosite'] = 'Returning to this web site?';
-$string['returntomaincoursepage'] = 'Return to main course page';
 $string['returntooriginaluser'] = 'Return to {$a}';
 $string['revert'] = 'Revert';
 $string['role'] = 'Role';
diff --git a/lib/coursecatlib.php b/lib/coursecatlib.php
new file mode 100644 (file)
index 0000000..30752ce
--- /dev/null
@@ -0,0 +1,2121 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Contains class coursecat reponsible for course category operations
+ *
+ * @package    core
+ * @subpackage course
+ * @copyright  2013 Marina Glancy
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class to store, cache, render and manage course category
+ *
+ * @package    core
+ * @subpackage course
+ * @copyright  2013 Marina Glancy
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class coursecat implements renderable, cacheable_object, IteratorAggregate {
+    /** @var coursecat stores pseudo category with id=0. Use coursecat::get(0) to retrieve */
+    protected static $coursecat0;
+
+    /** @var array list of all fields and their short name and default value for caching */
+    protected static $coursecatfields = array(
+        'id' => array('id', 0),
+        'name' => array('na', ''),
+        'idnumber' => array('in', null),
+        'description' => null, // not cached
+        'descriptionformat' => null, // not cached
+        'parent' => array('pa', 0),
+        'sortorder' => array('so', 0),
+        'coursecount' => null, // not cached
+        'visible' => array('vi', 1),
+        'visibleold' => null, // not cached
+        'timemodified' => null, // not cached
+        'depth' => array('dh', 1),
+        'path' => array('ph', null),
+        'theme' => null, // not cached
+    );
+
+    /** @var int */
+    protected $id;
+
+    /** @var string */
+    protected $name = '';
+
+    /** @var string */
+    protected $idnumber = null;
+
+    /** @var string */
+    protected $description = false;
+
+    /** @var int */
+    protected $descriptionformat = false;
+
+    /** @var int */
+    protected $parent = 0;
+
+    /** @var int */
+    protected $sortorder = 0;
+
+    /** @var int */
+    protected $coursecount = false;
+
+    /** @var int */
+    protected $visible = 1;
+
+    /** @var int */
+    protected $visibleold = false;
+
+    /** @var int */
+    protected $timemodified = false;
+
+    /** @var int */
+    protected $depth = 0;
+
+    /** @var string */
+    protected $path = '';
+
+    /** @var string */
+    protected $theme = false;
+
+    /** @var bool */
+    protected $fromcache;
+
+    // ====== magic methods =======
+
+    /**
+     * Magic setter method, we do not want anybody to modify properties from the outside
+     *
+     * @param string $name
+     * @param mixed $value
+     */
+    public function __set($name, $value) {
+        debugging('Can not change coursecat instance properties!', DEBUG_DEVELOPER);
+    }
+
+    /**
+     * Magic method getter, redirects to read only values. Queries from DB the fields that were not cached
+     *
+     * @param string $name
+     * @return mixed
+     */
+    public function __get($name) {
+        global $DB;
+        if (array_key_exists($name, self::$coursecatfields)) {
+            if ($this->$name === false) {
+                // property was not retrieved from DB, retrieve all not retrieved fields
+                $notretrievedfields = array_diff(self::$coursecatfields, array_filter(self::$coursecatfields));
+                $record = $DB->get_record('course_categories', array('id' => $this->id),
+                        join(',', array_keys($notretrievedfields)), MUST_EXIST);
+                foreach ($record as $key => $value) {
+                    $this->$key = $value;
+                }
+            }
+            return $this->$name;
+        }
+        debugging('Invalid coursecat property accessed! '.$name, DEBUG_DEVELOPER);
+        return null;
+    }
+
+    /**
+     * Full support for isset on our magic read only properties.
+     *
+     * @param string $name
+     * @return bool
+     */
+    public function __isset($name) {
+        if (array_key_exists($name, self::$coursecatfields)) {
+            return isset($this->$name);
+        }
+        return false;
+    }
+
+    /**
+     * All properties are read only, sorry.
+     *
+     * @param string $name
+     */
+    public function __unset($name) {
+        debugging('Can not unset coursecat instance properties!', DEBUG_DEVELOPER);
+    }
+
+    /**
+     * Create an iterator because magic vars can't be seen by 'foreach'.
+     *
+     * implementing method from interface IteratorAggregate
+     *
+     * @return ArrayIterator
+     */
+    public function getIterator() {
+        $ret = array();
+        foreach (self::$coursecatfields as $property => $unused) {
+            if ($this->$property !== false) {
+                $ret[$property] = $this->$property;
+            }
+        }
+        return new ArrayIterator($ret);
+    }
+
+    /**
+     * Constructor
+     *
+     * Constructor is protected, use coursecat::get($id) to retrieve category
+     *
+     * @param stdClass $record record from DB (may not contain all fields)
+     * @param bool $fromcache whether it is being restored from cache
+     */
+    protected function __construct(stdClass $record, $fromcache = false) {
+        context_helper::preload_from_record($record);
+        foreach ($record as $key => $val) {
+            if (array_key_exists($key, self::$coursecatfields)) {
+                $this->$key = $val;
+            }
+        }
+        $this->fromcache = $fromcache;
+    }
+
+    /**
+     * Returns coursecat object for requested category
+     *
+     * If category is not visible to user it is treated as non existing
+     * unless $alwaysreturnhidden is set to true
+     *
+     * If id is 0, the pseudo object for root category is returned (convenient
+     * for calling other functions such as get_children())
+     *
+     * @param int $id category id
+     * @param int $strictness whether to throw an exception (MUST_EXIST) or
+     *     return null (IGNORE_MISSING) in case the category is not found or
+     *     not visible to current user
+     * @param bool $alwaysreturnhidden set to true if you want an object to be
+     *     returned even if this category is not visible to the current user
+     *     (category is hidden and user does not have
+     *     'moodle/category:viewhiddencategories' capability). Use with care!
+     * @return null|coursecat
+     */
+    public static function get($id, $strictness = MUST_EXIST, $alwaysreturnhidden = false) {
+        if (!$id) {
+            if (!isset(self::$coursecat0)) {
+                $record = new stdClass();
+                $record->id = 0;
+                $record->visible = 1;
+                $record->depth = 0;
+                $record->path = '';
+                self::$coursecat0 = new coursecat($record);
+            }
+            return self::$coursecat0;
+        }
+        $coursecatrecordcache = cache::make('core', 'coursecatrecords');
+        $coursecat = $coursecatrecordcache->get($id);
+        if ($coursecat === false) {
+            if ($records = self::get_records('cc.id = :id', array('id' => $id))) {
+                $record = reset($records);
+                $coursecat = new coursecat($record);
+                // Store in cache
+                $coursecatrecordcache->set($id, $coursecat);
+            }
+        }
+        if ($coursecat && ($alwaysreturnhidden || $coursecat->is_uservisible())) {
+            return $coursecat;
+        } else {
+            if ($strictness == MUST_EXIST) {
+                throw new moodle_exception('unknowcategory');
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the first found category
+     *
+     * Note that if there are no categories visible to the current user on the first level,
+     * the invisible category may be returned
+     *
+     * @return coursecat
+     */
+    public static function get_default() {
+        if ($visiblechildren = self::get(0)->get_children()) {
+            $defcategory = reset($visiblechildren);
+        } else {
+            $toplevelcategories = self::get_tree(0);
+            $defcategoryid = $toplevelcategories[0];
+            $defcategory = self::get($defcategoryid, MUST_EXIST, true);
+        }
+        return $defcategory;
+    }
+
+    /**
+     * Restores the object after it has been externally modified in DB for example
+     * during {@link fix_course_sortorder()}
+     */
+    protected function restore() {
+        // update all fields in the current object
+        $newrecord = self::get($this->id, MUST_EXIST, true);
+        foreach (self::$coursecatfields as $key => $unused) {
+            $this->$key = $newrecord->$key;
+        }
+    }
+
+    /**
+     * Creates a new category either from form data or from raw data
+     *
+     * Please note that this function does not verify access control.
+     *
+     * Exception is thrown if name is missing or idnumber is duplicating another one in the system.
+     *
+     * Category visibility is inherited from parent unless $data->visible = 0 is specified
+     *
+     * @param array|stdClass $data
+     * @param array $editoroptions if specified, the data is considered to be
+     *    form data and file_postupdate_standard_editor() is being called to
+     *    process images in description.
+     * @return coursecat
+     * @throws moodle_exception
+     */
+    public static function create($data, $editoroptions = null) {
+        global $DB, $CFG;
+        $data = (object)$data;
+        $newcategory = new stdClass();
+
+        $newcategory->descriptionformat = FORMAT_MOODLE;
+        $newcategory->description = '';
+        // copy all description* fields regardless of whether this is form data or direct field update
+        foreach ($data as $key => $value) {
+            if (preg_match("/^description/", $key)) {
+                $newcategory->$key = $value;
+            }
+        }
+
+        if (empty($data->name)) {
+            throw new moodle_exception('categorynamerequired');
+        }
+        if (textlib::strlen($data->name) > 255) {
+            throw new moodle_exception('categorytoolong');
+        }
+        $newcategory->name = $data->name;
+
+        // validate and set idnumber
+        if (!empty($data->idnumber)) {
+            if (textlib::strlen($data->idnumber) > 100) {
+                throw new moodle_exception('idnumbertoolong');
+            }
+            if ($DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
+                throw new moodle_exception('categoryidnumbertaken');
+            }
+        }
+        if (isset($data->idnumber)) {
+            $newcategory->idnumber = $data->idnumber;
+        }
+
+        if (isset($data->theme) && !empty($CFG->allowcategorythemes)) {
+            $newcategory->theme = $data->theme;
+        }
+
+        if (empty($data->parent)) {
+            $parent = self::get(0);
+        } else {
+            $parent = self::get($data->parent, MUST_EXIST, true);
+        }
+        $newcategory->parent = $parent->id;
+        $newcategory->depth = $parent->depth + 1;
+
+        // By default category is visible, unless visible = 0 is specified or parent category is hidden
+        if (isset($data->visible) && !$data->visible) {
+            // create a hidden category
+            $newcategory->visible = $newcategory->visibleold = 0;
+        } else {
+            // create a category that inherits visibility from parent
+            $newcategory->visible = $parent->visible;
+            // in case parent is hidden, when it changes visibility this new subcategory will automatically become visible too
+            $newcategory->visibleold = 1;
+        }
+
+        $newcategory->sortorder = 0;
+        $newcategory->timemodified = time();
+
+        $newcategory->id = $DB->insert_record('course_categories', $newcategory);
+
+        // update path (only possible after we know the category id
+        $path = $parent->path . '/' . $newcategory->id;
+        $DB->set_field('course_categories', 'path', $path, array('id' => $newcategory->id));
+
+        // We should mark the context as dirty
+        context_coursecat::instance($newcategory->id)->mark_dirty();
+
+        fix_course_sortorder();
+
+        // if this is data from form results, save embedded files and update description
+        $categorycontext = context_coursecat::instance($newcategory->id);
+        if ($editoroptions) {
+            $newcategory = file_postupdate_standard_editor($newcategory, 'description', $editoroptions, $categorycontext, 'coursecat', 'description', 0);
+
+            // update only fields description and descriptionformat
+            $updatedata = new stdClass();
+            $updatedata->id = $newcategory->id;
+            $updatedata->description = $newcategory->description;
+            $updatedata->descriptionformat = $newcategory->descriptionformat;
+            $DB->update_record('course_categories', $updatedata);
+        }
+
+        add_to_log(SITEID, "category", 'add', "editcategory.php?id=$newcategory->id", $newcategory->id);
+        cache_helper::purge_by_event('changesincoursecat');
+
+        return self::get($newcategory->id, MUST_EXIST, true);
+    }
+
+    /**
+     * Updates the record with either form data or raw data
+     *
+     * Please note that this function does not verify access control.
+     *
+     * This function calls coursecat::change_parent_raw if field 'parent' is updated.
+     * It also calls coursecat::hide_raw or coursecat::show_raw if 'visible' is updated.
+     * Visibility is changed first and then parent is changed. This means that
+     * if parent category is hidden, the current category will become hidden