* Fixed a bug in the MySQL implementation of User data source's getGroupName() method. It treated 'row' as an array ref instead of a normal array.
* Made the MySQL User data source automatically add a group with ID 1, named 'Administrators'.
* Special-cased the group with ID 1 in the user object so that if a user is in that group, he automatically is assumed to have all rights.
* Fixed a bug with the saving of the original groups in the user object (the backup object was simply a reference to the original object, which was later changed in place, causing the backup to change too).
* Fixed the handling of rights in the user object constructor to simply set the rights in one line instead of using indirection.
* Fixed the invalidateRights() method of the user object to correctly re-set the rights (previously it was not turning the generated array into a hashref).
* Added a setup.install implementation to the login component. It now queries the user for an administration username and creates the relevant user and puts it in the magic group 1.
* Fixed a call to checkAddress() in the login component.
* Added some XXX comments.
* Changed the way text nodes are handled so that adjacent text nodes are merged.
* Changed the way original keys are kept so that they work across flattening, rounding, and that there is no 'coses: original keys' hash getting in the way.
* Fixed a bug whereby empty arrays and hashes caused extraneous iterations in <set> elements.
* Removed some redundant scalar()s.
* Added support for group membership levels (now you can be a normal member, as before, but also a group op or a group admin) -- this affected the user data source, the mysql data source, and the user service.
* Added a getFieldsHierarchically() method to the user data source.
* Added a getFieldsByCategory() method to the user data source.
* Fixed a typo in some of the SQL in the rights management part of the MySQL user data source.
* Added support in the arguments input module for treating the arguments as dot-delimited trees and returning a specific branch.
* Added support in the passwords service for encrypting a specific password.
* Added a getFieldByID() method to the user service.
* Made the groups in the user service be indexed by both ID and Name.
* Implemented the user preferences component.
* Changed FileStrings so that it doesn't have to be updated every time the file string format changes. The format is now one line per piece of metadata, then a blank line, then the string data. If additional metadata is added later then this will automatically support it.
* Renamed the dataSource.strings service to dataSource.strings.customised, and renamed its get() method to getCustomisedString().
* Changed the semantics of dataSource.strings.customised so that it no longer looks for a default string if it can't find a customised one (and thus removed getDefaultString).
* Abstracted the Generic output module even more. It now consists of output.generic (a service instance with its own constructor) and dataSource.strings (a pure service), the latter of which is a wrapper around dataSource.strings.customised and dataSource.strings.defaults.
* Updated Coses to work with the new dataSource.strings insterface.
* Removed the test app DataSource::ConsoleStrings and DataSource::HTTPStrings files, since they were redundant with the default output files.
* Removed all the default strings in the CosesEditor and Login components since they are pretty pointless.
* Factored out the call to dump() in the GenericOutputs module.
* Changed setString in the MySQL string data source so that it will now add a blank string (it used to delete the string if it was blank, but that meant that it was not possible to customise strings away).
* Added a piece of metadata to strings: their version number.
* Updated the customised string data source stubs to mention the version data now stored with all strings
* Added a getAllStringVersions method to the customised strings data source which returns all the string names and their version numbers.
* Made the customised strings data source check the version number of every string in its database during setupInstall to make sure that they are all up to date, version-wise. If any are out of date, the user is notified.
* Added support for the new version column to the MySQL version of the customised string data source.
* Updated the increasingly misnamed CosesEditor to support the versioned strings stuff.
* Factored out some code in the CosesEditor.
* Added version information to all default strings. All default strings are now at version 1.
* Fixed a typo in a FileStrings dump statement.
* Fixed the calls to setProgress in the MySQL user and strings data sources and in the user field factory to use the correct syntax (a parsable dotted string instead of unlocalisable plain English).
* Updated the Generic output module's documentation to match what now happens.
Corrected a problem with scope and the 'escape' attribute of <text> elements.
Renamed 'shrink' and 'expand' to the slightly more accurate 'flatten' and 'rounden'. (ahem)
Changed the ordering of symbol unescaping in roundening to be less (not?) susceptible to incorrect expansion.
Changed the ordering of symbol escaping in flattening to be consistent with the unescape order when roundening.
Made <text> trim one leading and one trailing newline in text nodes if xml:space="default" (makes COSES files neater).
Added a 'coses: original keys' hash to values whose keys are sanitised
Made the 'escape' values be lower case instead of upper case (makes COSES files neater).
Clarified some of the text.
Made the use of quotes more consistent.
Wrote Chapter 5, "USING GET SERVICE".
Added an empty Chapter 10, "THE SERVICE REFERENCE".
* Changed the COSES editor to support the abstracted logic mentioned above (note: maybe "COSES editor" is no longer an apt name...).
* Made the parseNS method of the XML service actually do something.
* Made the COSES processor namespace aware.
* Changed <coses:embed string=""> to <coses:include href="">. I was going to change it to an XInclude, but it turns out the processing model is wrong for that (XInclude, for instance, bans recursive nesting). Added support for a 'parse' attribute just like for XInclude (support three values: 'xml', 'text' and 'x-auto').
* Added columnExists() to the DBI database helper.
* Added string caching to the strings data source. Should be particularly helpful for Apache mod_perl installations.
* Added more comments to a few modules
* Changed the semantics of adding and removing user fields ("user data types")
* Implemented a factory service to perform those semantics ('registerField', 'removeField' and the convenience 'registerSetting')
* Added a missing return in the MySQL implementation of the user data source
* Added an assert in the MySQL version of 'addRight' to catch an easy error
* Added XXX comment in the MySQL version of 'addRight' (will fix this in the next patch)
* Fixed a case where sending an undefined value to the DBI database implementation would fail during the untainting
* Fixed MySQLID to actually work (in the DBI ResultsFrame)
* Fixed two typos in the user service ('settings' to 'setting')
* Added the requirement that a user must have certain rights to use the COSES editor
* Added a setupInstall implementation to the COSES editor
* Made the start of an execution start clearer in the error log. This depends on the debug level; just makes it easier to determine what is a single execution block in a 70MB error log.
* Changed a stray semicolon into a comma, and fixed other misspellings or typos.
* Added the default strings to the CosesEditor component.
* Added some comments to various files indicating work to do and documenting some methods
* Fleshed out the Strings Data Source API (and implemented it in the MySQL version)
* Added the name "MySQL" to the MySQL-specific ID method of the Database Helper
* Factored out the XML parser dependency into an XML service
* Added a generic content sink implementation to the XML service
* Added a convenience method for manipulating user settings
* Added and implemented all the COSES commands
* Removed trailing spaces
* Implemented MagicCollectingArray, which acts like MagicPipingArray except that the result is collected into one big array instead of multiple arrayrefs
* Added the relevant getCollecting*List methods to the Controller class
* Added DESTROY methods to the magic array to prevent DESTROY method calls from being propagated
* Made the debug.dumpVars string more robust when the data hash includes one of the three magic characters '(', '.' or ')'
* Implemented sanitation in COSES so that any keys in the data hash containing one of the three magic characters '(', '.' or ')' will have them transliterated to '[', ':' and ']' respectively (sanitation is optional and can be bypassed)
* Changed the API of the generic output module's 'output' method to take the optional $session argument last, thus simplifying the call sites a lot
* Implemented the 'dispatcher.output' service in several classes to return strings that must be support in variants (required for a good UI in the COSES editor)
* Renamed the Login module's strings so that they use the dot-notation separating significant parts
* Added implicit data to the hash passed to the string expander from the generic output service
* To support the implicit data, added a |hash| stub method to the Session.pm module (and changed the already implemented version in the descendant user service to take this into account)
* To support the implicit data, added |hash| and |getArguments| methods to the Input.pm module (and implemented |getArguments| in the Arguments.pm module)
* To support the implicit data, made the Program.pm module keep track of the executing command
* To support the implicit data, added |hash| and |name| methods to the Program.pm module (actually, |name| was already assumed to exist in other parts of the codebase)
* Corrected trivial semantic mistake in Program.pm's |verifyInput| method
* Added a way to get a user not by username but by a specific address (modifies the user service and the user data source and it's MySQL implementation)
* Implemented GenericOutputs.pm, an implementation of 'dispatcher.output.generic' and 'dataSource.strings.default' for some strings used by other parts of PLIF (currently only supports 'stdout' and 'http' protocols, and minimally at that)
* Implemented Login.pm, a helper module that implements user authentication, user creation and the sending of a new password if it is forgotten, a utility method |hasRight| to ensure that the authenticated user has a particular right (resulting in a message if not), and various routines to support all this.
* added addUserGroup and removeUserGroup to the data source interface for users
* added some comments
* changed many double quotes |"| to single quotes |'| where double quotes were not needed
* implemented the remaining methods for the MySQL implementation of the user data source
* added a mode flag to user fields (idea from MattyT) so that fields can be disabled, hidden, etc
* Started implementing the MySQL version of the user data source (completed so far: database creation and a few of the SELECT calls)
* Changed the user property 'disabled' to 'mode' to enable extensibility
* Fix a potential bug with changing addresses (make sure the fake session object doesn't update the database -- this is an incomplete solution so far, fields still have the bug)
* Moving 'typeData' around in the order of the user field constructor arguments to match the User data source's 'getField*' methods so that, again, they map more directly
* Renaming the 'getFieldFrom*' methods to 'getFieldBy*' to be consistent with 'getUserBy*'
* Implemented UserFieldFactory.pm
* Implemented Passwords.pm
* Added a comment to DataSource/User.pm explaining how (typically) to search for a username
* Fleshed out the DataSource/User.pm API by adding some schema management methods
* Added notes on which fields in the database schema should be keys
* Added comment to Service/User.pm noting the difference between Objects, Services, and Service Instances
* Changed 'user.field.factory' to 'user.fieldFactory' to prevent a namespace collision with 'user.field.(type)'
* Calling 'insertField' in one case which I missed when adding the method
* Implemented 'hash', 'joinGroup', 'invalidateRights', 'writeProperties' and 'writeGroups'
* Changed Service/UserField.pm so that one user field class can be used for any category
* Added a 'username' convenience method and implemented 'write'
* Added a comment to Service/UserFieldFactory.pm explaining how it should work
* Removed the 'user.field.generic.generic' field implementation, replaced it with a simpler 'user.field.string' implementation
Added some comments to various user-related files.
Factored out some code that started becoming common when inserting fields in Service/User.pm.
Added code to deal with adding new contact details.
Added code to support removing fields from a user.
In addition to the code that actually does stuff, I still need to add a MySQL implementation of the data source and the Field class and its associated Factory.
Also added Passwords.pm (stubs for a password generator and encryptor) and made Session objects store a pointer to the controller object.
Added an optimisation to Controller.pm so that service names will be hashed once accessed. This should make multiple accesses of the same service a lot quicker. To go with this I added some diagnostics code (on exit) and moved the code around a little bit.
Fixed a minor transgression of the coding style guidelines in Magic*Array.pm. :-)
Neatened up the code in Output.pm.
Made it so Session objects assume they have an 'app' property, and so don't need to be passed $app all the time. (Sessions are objects now.)
Added an unimplemented getAddress() method to the Session class.
* factored out some of the method dispatching code by adding a dispatchMethod() method to the controller;
* turned the Dispatcher class into simply a function on the base Service class and removed Dispatcher.pm;
* made it possible for services to be both services and objects and provide different services depending on which context they were called in (and used this to make the AdminCommands module actually do what it was intended to in the first place, namely, only work for CommandLine access);
* fixed it so if a service is first created by getServiceList the constructed version will actually be cached;
* made output more generic by allowing services to implement arbitrary parts of the output API, used that to make AdminCommands usable without requiring additional code to support it;
* added some documentation;
* added some dump(10) statements to help debugging;
* fixed the string datasource SQL;
* fixed the DBI database so it can handle errors;
* added tableExists API to the DBI database helper.
Thanks to myk, justdave and zach for some ideas.