Bricolage Element System Redesign Technical Specification
This document outlines the technical details for implementing the element revision described in ElementRevision::FunctionalSpec. While that document explains what the revision will look like, this one explains the details of the API related to data structures. For implementation details, see ElementRevision::Implementation.
For all practical purposes, this document may be considered a working draft toward the design of features for Bricolage 2.0. It goes beyond what's specified in the Functional Specification by outlining a new foundation of APIs to which all of Bricolage will eventually be migrated.
Thus, the changes to the Bricolage API will manifest in several different categories of object classes, each of which will be described below. We start with the fundamentals.
Bricolage 2.0 will be built on an architecture defined as follows:
configure,
no make.
This is the base class from which all Bricolage classes inherit. At a minimum,
it will define an interface for automating the creation of attribute
accessors. The module that will be used for this purpose is Class::Meta, a
module I've been developing for some time, based on the old my_meths() and
register_properties() functions in 1.x, but much more useful. Class::Meta
will provide a simple, extensible interface that manages:
AUTOLOAD!
The common API that Bricolage will provide for all of its base classes includes the following methods:
None.
All Bricolage 2 data classes will now inherit a common API, Bricolage::Biz. This class serves two purposes: First, it defines an interface common to all Bricolage business objects, where ``Bricolage business objects'' is defined as objects that store persistent data. And second, it defines and provides the complete data access interface (really a pass-through to Bricolage::Store).
The fundamentals of this common API are as follows:
save() is called (such as when a user has provided a non-unique
value for a unique attribute). This design is based on two assumptions: That
users should be able to input just about any data and only have their changes
checked when they save a business object; and that many such errors will be
caught at the data store level (e.g., by constraints and unique indexes).
clone() method is called on an object, a deep copy of the object will be
returned as a brand new object with a new GUID. This will simplify numerous
administrative tasks, as well as the cloning of content objects (such as
documents).
That Bricolage::Biz persistence API will include the methods lookup(),
search(), list_guids(), count(), and save(). See
Bricolage::Store for more information on the persistence
API. The important thing to note here is that the persistence API will be
invisible to users of Bricolage::Biz classes. How the data is actually stored
and retrieved will mainly be an implementation or perhaps installation
configuration detail.
The common API Bricolage::Biz establishes for all Bricolage 2 objects will consist of the following interface:
Any objects inheriting from Bricolage::Biz that contain *_guid attributes
(such as site_guid or user_guid) will have, in addition to the usual
accessors (such as get_site_guid() and set_site_guid()), an extra
accessor to return the actual object (such as get_site()) and an accessor
to set with an object (such as set_site(), which of course has the side
effect of changing the corresponding GUID.
This functionality is available in Perl via the Data::UUID module.
GUIDs will be stored in the data store as a string representation of the GUID
proviced by the create_str() in the Data::UUID class.
The default new() constructor will be inherited from Bricolage.
search() should the same search parameters be passed. See
Bricolage::Store for details.
if ($bric->can_do(EDIT)) {
# Access allowed.
}
Returns a true value of the current use has at least the perimssion specified in the argument. This method will be used in the UI to provide apporopriate access to an object. Internally, this means that, for optimal efficiency, every Bricolage object must be loaded from the database with its ACL.
save() will be encapsulated in a single
transaction. Cascading calls to the save() methods of contained objects
will be a part of a single transaction, so that one transaction manages to
save changed to all or none of an object and all of its contained objects.
This class will handle all of the work necessary to communicate with back-end storage systems. Bricolage::Store itself is an abstract class; its subclasses will implement its interface for different storage devices: RDBMSs, OODBMSs, XML Files, LDAP, or whatever. The initial target persistence back end will be PostgreSQL 7.4, but MySQL 4.1 support may be developed concurrently. These will be implemented as Bricolage::Store::DBI::Pg and Bricolage::Store::DBI::mysql. Others, such as the likely Bricolage::Store::LDAP, will be added later.
A few notes on the implementation of this class are relevant here. First, the
API has been designed in such a way as to offload as much of the work of
selecting, sorting, and retrieving data to the back-end system as possible.
This is done is by preventing instantiation of thousands or millions of
objects at once by using a combination of features, including a collection
(a.k.a. iterator) class to only fetch objects from the store and instantiate
them as they're needed, and new Limit and Offset parameters to
search(). See the Bricolage::Util::Collection manpage for more about the collection
API.
But even more importantly, I want to move permission checking to the storage device, as well. From the point of view of an RDBMS, this means that we will have to change the way permissions work so that they can be easily and efficiently checked by the use of SQL joins or similar approach. But ultimately, that's an implementation detail, and if necessary, we can start out by doing the permission checking the old fashioned way, but still behind the API, so that we can later refactor it to use the database. What this means is that the current user object will always have its GUID implicitly used by the storage API to limit the number of objects returned to those that the user has READ or greater permission to access.Fundamentally, this means that we'll likely have to change the DENY permission in such a way that it always trumps other permissions. This will be the truly hard part.
In the end, the upshot wiil be that any call to search() or list_guids()
will return only those objects to which the current user has at least READ
access.
This class will handle Bricolage configuration. Configurations are kept in a file, and can only be changed by restarting the server after changing the file. Thus it makes sense to continue to use such configurations as Perl constants in order to maximize performance. The interface has yet to be determined, but the simplest approach may be to keep the current interface. Choices of other configuration file formats will be entertained (the Apache httpd.conf-like format comes to mind, as does a pure Perl format; see Config::ApacheFormat and Myco::Config for examples).
This class provides access to objects that need to be universally accessed. Specifically, it provides access to the currently logged-in user object, the system-wide shared cache, and the curent localization object. It should be assumed that nothing can be done with the Bricolage API without first logging in as a user.
This class defines all the Bricolage exceptions. It will be defined by Exception::Class, and will provide exportable functions that can be used to easily throw exceptions. Functionally it will be similar to Bricolage 1.x's Bric::Util::Fault, but without the legacy baggage from the pre-Exception::Class version of Bricolage exceptions. All exception messages will be localized by an instance of the Bricolage::Util::Language class. Bricolage::Util::Exception::Fatal exceptions will be thrown for developer or configuration errors; all other non-fatal errors will be thrown for user errors, such as when a non-unique value was entered by a user for a unique attribute.
Defines the various states of Bricolage business objects. It will export
constants that can be used with the state attribute accessors of
Bricolage::Biz-inherited objects. These constants will return singleton
objects that overload == to facilitate comparison of states, and "" to
return a localized string representing the state.
Defines the various priorities of Bricolage workflowable objects. It will
export constants that can be used with the priority attribute accessors of
Bricolage::Biz::Decorator::Workflowable-inherited objects. These constants will
return singleton objects that overload == to facilitate comparison of
states, and "" to return a localized string representing the state.
Locale::Maketext-inherited localization module. This class and its subclasses
will be used to generate all hard-coded text emitted by Bricolage,
including text in exceptions. We will not be using the _AUTO key; all
strings will be required to be present in at least the en_us language
class, from which all other classes inherit. This requirement makes it easier
to keep en_us canonical and up-to-date.
Complete Bricolage DateTime support, inherited from DateTime. Any methods or values that are date or time or date and time values will return objects of this class or one of its subclasses. Bricolage::Util::DateTime will handle all aspects of time zone translation, since all dates and times in Bricolage are stored in UTC in the database. By default, Bricolage::Util::DateTime objects will be set to the value of the Date/Time preference. Bricolage::Store will change it to UTC before saving values to the data store.
Bricolage::Util::DateTime will also have stringification overloaded so as to facilitate saving to the data store and to simplify outputting values in templates.
This class provides the interface for Bricolage sessions, which are associated with user objects. The data will be stored in the database, and the interface for accessing the session data will be entirely object-oriented (that is, no tied hash will be exposed as with Apache::Session). It will also support the concept of contexts, so that different parts of Bricolage can use their own contxts in the session without fear of tromping on the data used by other parts of Bricolage.
Bricolage::Util::Session will most likely be a wrapper class around a series of sessions, instead of one big session. Each sub-session will represent a single context. This approach optimizes for the most common case, in which Bricolage needs to access only a single context's session in a given request, and therefore eliminates the need to load a monolithic session with considerable data from other contexts that will never be used in a given request.
The idea of contexts also allows for us to develop a continuation-style Web application. Because the current context will be tracked for every page, it will also allow multiple browser windows to be open, as each will have its own context. If two windows enter the same context, the one that submitted a request most recently will ``win''; the other window will be synced to the status of the first window upon its subsequent request.
Furthermore, each context will have a ``state'' and a ``state history''. This will enable the user to use the back-button in the browser. When she does, and submits a form that was submitted, say, 3 requests ago, because the context and state of that page was included in its request, the application will be able to revert to that state without triggering an error. If she goes back past the point where the context state was cleared (for example, before a ``save''), a message will be sent with the response stating that the state is no longer valid, and the page will render in the current state.
All of this is of course dependent upon any given request containing its context and state. So every form submit and/or URL + GET string should contain this data:
<input type="hidden" name="cx" value="context+state" />
This section will likely see further expansion as we continue to consider the benefits of continuation-style UI design.
This class manages a system-wide, interprocess shared cache for Bricolage, and
its instance will normally be retreived from
Bricolage::Util::Context->cache. This object will be useful for caching
commonly-accessed but rarely changed data. We expect that the data in this
object will be volatile and will not persist between shutdowns.
This is the abstract base class for Bricolage's plugin authentication system. At a minimum, its concrete derived classes will be Bricolage::Util::Auth::DBI and Bricolage::Util::Auth::LDAP. These may use the Bricolage::Store API for data access. The concrete implementation used for authentication will be determined by a Bricolage::Util::Config setting. This class will be only be used internally by Bricolage::Biz::Party::Person::User.
Bricolage::Util::Auth->login($user, $password);
Authenticates a user against an authentication back-end. Returns true on
success and throws an exception on failure. Called by
Bricolage::Biz::Party::Person::User->login.
Bricolage::Util::Auth->change_password($user, $password);
Changes a user's password in the authentication back-end. Returns true on
success and throws an exception on failure. Called by
Bricolage::Biz::Party::Person::User->save.
Bricolage::Util::Auth->change_username($user, $username);
Changes a user's username in the authentication back-end. Returns true on
success and throws an exception on failure. Called by
Bricolage::Biz::Party::Person::User->save.
A true collection class that iterates over its collection of objects, fetching them from the data store only when they're needed by the user, thus cutting down on memory usage. At least that's the plan. Even if we don't do that at first, the API will work as if we had, so we can always refactor it later and thus come to enjoy the efficiency gains.
The Bricolage::Store search() method will always return a collection
object. For the purposes of convenience and backwards compatibility, the
collection class will also be implemented as a tied array, so that users can
simply use it like an array reference if they wish, while retaining the
efficiency benefits of a true collection. See the Apache::FakeTable manpage for an
example of a class that doubles as a tied variable.
A subclass of Class::Meta::Class, this class provides additional information about every Bricolage class, including its GUID (all Bricolage classes have a record in the database for building relationships) and their localized names, descriptions, etc. Replaces the old Bric::Util::Class.
This is an abstract base class defining the interface for mediating the relationship between two objects in a ``has many'' pattern. That is to say, it represents the relationship between the two objects.
In many cases of a has-many relationship, Bricolage::Biz::Mediator will not be used, because other than identifying the two related objects, no other data is needed. However, in those situations when the relationship itself requires extra data--such as some rules about the relationship--a subclass of Bricolage::Biz::Mediator will be used.
This subclass mediates the relationship between element types and subelement types. It is necessary because the relationship tracks the element constraints and occurrence specifications for subelements.
Note that the name and description attributes inherited from
Bricolage::Biz can just return the value of the name and the description of
the child element, but can't set it.
Mediates the relationship between a document type and a site in which documents of that type can be created or aliased.
output_channels().
document_type_guid attribute can be published. The primary output channel
will always be the first output channel in the collection.
This object mediates the relationship between document type/site associations (as defined by Bricolage::Biz::Mediator::DocumentTypeSite) and output channels. A mediator object is required because an output channel may or may not be associated with a new document of the document type when a new document is created. [Is that as clear as mud?]
This object mediates the relationship between alert types and the parties that will receive alerts.
This object mediates the relationship between alert types and the groups of parties that will receive alerts.
This class replaces the old Bric::Util::Grp, and is used for managing groups
of objects in Bricolage. The interface can be simplified a great deal over the
existing interface. For example, it does not have to support inherited groups,
since this is a feature we've never used (though we can always add it later,
if necessary). The ``permanent'' attribute will be replaced with a ``PERMANENT''
value for the state attribute (See
Bricolage::Util::State) to indicate groups that
should never be deleted, such as the ``All'' groups. The ``secret'' attribute
might be able to go, depending on our needs. Unlike the original Bricolage 1.0
design, no Bricolage 2.0 groups should really be completely internal, and thus
don't need to be hidden. But we'll have to see what the implementation
requires. Attributes and member attributes can definitely go.
Of course, the primary use of groups is for assigning permissions to groups of objects. Some groups will need to hold objects of different types, such as the groups of objects maintained by Desk, Workflow, Category, and Site objects, which can hold anything desks and workflows and categories can hold (Documents and Templates, as of this writing). There may be a way to siplify this by allowing groups to hold objects of only one class (simplified Group class design), and then have Desk, Workflow, Category, and Site use separate groups for each of the types of objects they need to maintain.
As I said, the primary use of Groups is to group objects for granting permissions. This functionality can probably be largely ported from 1.x, but see the notes on Bricolage::Biz::Permission for more details.
One thing we need to keep in mind is how to handle limited permissions so that users can't give themselves greater permissions than they already have. This means that no user should be able to add herself to a user group with greater permissions than she already has. Nor should she be able to increase the permissions of a user group of which she's a member, or of a group to which any user group she's in has been granted permission.
To manage this, I think that these rules should apply:
The rules of thumb are: A user can never grant herself or any other user greater permissions than she herself has, either through group membership or through permission changes.
This object manages permissions granted to groups of users to access groups of objects. It might need some more thought as to how it will integrate with Bricolage::Biz::Group and Bricolage::Biz::Party::Person::User, but we can start out by simply porting it at first.
The permissions supported will be ``READ'', ``EDIT'' (assumes READ), ``CREATE'' (assumes EDIT) and ``DENY''. The highest priority permission will trump other permissions. If a user has multiple permissions to access an object due to multiple group memberships, then the highest-priority permission will be the permission granted to that user. Note that DENY has the highest priority; so if a user has ``EDIT'' and ``DENY'' permissions to access an object, he will be denied access.
Note that groups of workflowable objects (that is, objects that can be managed in workflow) can have additional permissions applied to them, ``RECALL'', which allows the user to recall an object from the library; and ``PUBLISH'', which allows the user to publish the document or deploy the template.
The permission priorities, from lowest to highest, are as follows:
This class manages system-wide preferences. It can largely be ported from Bricolage 1.x.
This class manages userñ preferences. It can largely be ported from Bricolage 1.x.
This class is the base class for all persons and organzations. It handles the interfaces that are common to both people and organizations, such as managing contacts and addresses. The term was selected after reading ``Data Modeling Patterns,'' By David C. Hay (http://www.dorsethouse.com/books/dmp.html).
This is the base class representing all person objects in Bricolage, currently meaning users and contributors. Data for all persons will be managed centrally. That is to say, if a person is both a user and a contributor, the same data for the base class attributes and relationships will be used for both types of objects.
Lingua::Strfname::strfname(). The set_name() method will be overridden
to throw an exception.
This class inherits from Bricolage::Biz::Party::Person and extends it by
providing an authentication and session management interface. The session
object's data will be stored in the data store along with other user data. See
Bricolage::Util::Session for details. The user's
login status will be tracked by comparing the current time to the time they
last authenticated. If the time exceeds the AUTH_TTL
Bricolage::Util::Config setting, the user will be considered logged out.
Note that the Bricolage API makes no attempt to verify the validity of the
client logging in. It is assumed that the validity will have been confirmed by
the code calling authenticate(). For exampe, Bricolage::Server::Apache must
take care that its authentication cookie hasn't been tampered with.
Bricolage::Biz::Party::Person::User will use a subclass of Bricolage::Util::Auth to athenticate users and enable the changing of usernames and passwords.
my $session = $user->session;
User's Bricolage::Util::Session object.
$user->authenticate;
Authenticates the user to Bricolage, sets the last_access time to the
current time, and sets the user object as the object to be returned by
BricolageUtil::Context->current_user. Throws an exception if the user's
last access was longer ago than the number of seconds specified by the
AUTH_TTL Bricolage::Util::Config setting.
$user->login($password);
Logs the user into Bricolage. Throw an exception if the authentication
fails. Sets the last_access attribute to the current time and calls
authenticate() upon success. Internally, this method uses a subclass of
Bricolage::Util::Auth to handle the authentication.
$user->logout;
Logs the user out and sets the last_access attribute to undef.
This class defines the interface for Bricolage contributors. Contributors are defined by a Bricolage::Biz::Type::Model::Contributor object. The old ``Role'' feature from Bricolage 1.x will instead by handled via Input Channels [This may or may not actually work, because the number of possible Input Channels is determined by the Bricolage::Biz::Type::Model::Contributor object. It's not unlimited as is the number of ``Roles'' per contributor in 1.x. But how many people really use this feature?] A single person can be multiple contributors, in that they may contribute different types of content and thus be different types of contributors. For example, one person may be both a ``Photographer'' contributor type and a ``Writer'' contributor type. She may even be a user. But regardless, the record is always based the same person with the same data inherited from Bricolage::Biz::Party::Person.
The profile for Bricolage::Biz::Party::Person::Contributor will display custom subelements defined by the contributor type object. The interface will be identical as that used for the element hierarchy of documents. See Bricolage::Biz::Document for more information.
This class inherits from Bricolage::Biz::Party, and represents an organization. Its additional interface is simple.
name attribute inherited from
Bricolage::Biz is assumed to be the formal name for an organization. Thus if
the name is ``Apple Computer, Inc.'', the short_name might be ``Apple''.
This class manages sources of content, that is, organizations that are the
sources for content, such as wire services or stock photo warehouses. Its
expire_interval attribute may be used to automatically expire content after
a certain period of time. It is associated with an organization, and more than
one source can use the same organization (because they could have different
expiration rules for different content they supply.
undef (or any
false value, really), meaning that content from the source need not be
expired.
A method of contacting a party, such as phone, email, instant message, etc. Contacts are defined by contact types, Bricolage::Biz::Type::Contact.
A party's address, associated with a Bricolage::Biz::Placement. Addresses are defined by address types, Bricolage::Biz::Type::Address. Implementation of this class can be deferred.
The object defining the relationship between a party and one ore more addresses. Think of the party's placement as where it's located. This might be ``Home'' or ``Work'' or ``Corporate Headquarters''. Some may have more than one address, such as shipping and billing. And finally, a party can have more than one placement of a particular type over time. For example, there may be a ``home'' placement for a person until a particular date, and then a different ``home'' placement starting on a later date. This ensures the keeping of a history of placements for a party over time.
This namespace is use for classes that specify the type for an object of a given class. So a Bricolage::Biz::Document object has an associated Bricolage::Biz::Type::Model::Document, and a Bricolage::Biz::Element object has an associated Bricolage::Biz::Type::Element, etc. All classes in this namespace will inherit from Bricolage::Biz::Type.
This class is used to determine the media types (formerly known as ``MIME
types'') of files uploaded to Bricolage, as well as for elements. It can
largely be ported from Bricolage 1.x's Bric::Util::MediaType. Note that the
get_type_by_ext and get_guid_by_ext constructors from that class can be
eliminated, since one can use search() and list_guids(), instead. The
only difference is that the consumer of the class must pass in a suffix
parameter, not a full filename.
This is the base class for element types. These define the structure of elements, which are used to add content to documents, contributors, and categories. It defines the interface for its inherited classes, so that any part of an element tree (branch or leaf, container or field) can be handled identically.
This is a concrete derived class of Bricolage::Biz::Type::Element whose
objects function as container element type specifications. Container element
types are the branches in an element type tree, and can contain fields
(Bricolage::Biz::Type::Element::Field) or other container types as
subelements. Its interface is identical to that of
Bricolage::Biz::Type::Element. Its widget_type_guid attribute always points
to a Bricolage::Biz::Type::Widget::Profile object. Its value type will always
be undefined.
This is a concrete derived class of Bricolage::Biz::Type::Element whose
objects function as field element type specifications. Field elements types
are the leaves in an element type tree, and contain exactly one value and no
subelements. Its interface is identical to that of
Bricolage::Biz::Type::Element. However, its ordered_subelement_types(),
unordered_subelement_types(), and subelement_types() methods always
return undef.
This abstract base class defines the struture of Bricolage element values. Although Bricolage defines a limited number of element values, all defined as subclasses of Bricolage::Value, because element administrators can set up constraints for the particular value to be used in a particular element, we needed an object to store that data. Each subclass will have a corresponding Bricolage::Value subclass.
The initial Bricolage::Biz::Type::Value subclasses wil be:
class_guid field for the
Bricolage::Biz::Class object defining the class of Bricolage business object
that the value links to.
precision attribute that
defines how precise the datetime objects can be. The options are:
This abstract base class defines the structure of widgets to be used for displaying elements. Although Bricolage defines a limited number of widgets, all defined as subclasses of Bricolage::UI::Widget, because element administrators can set up constraints for the particular widget to be used in a particular element, we needed an object to store that data. Each subclass will have a corresponding Bricolage::UI::Widget subclass.
The subclasses of Bricolage::Biz::Type::Widget will often add to the following simple interface.
help_text and display_size.
help_text, rows, and cols.
help_text.
help_text and either value_options or class_attribute.
help_text and checked.
help_text and checked.
help_text.
help_text.
help_text.
help_text.
help_text.
help_text and
class_attribute.
help_text and display_size and either value_options or
class_attribute.
help_text and
class_attribute.
help_text and display_size and either value_options or
class_attribute.
help_text and value_options.
This class is the base class for those types whose instances are associated with an element tree and with input channels. It defines the relationship between a container element type, which defines the structure of the document; the sites in which objects based on a model can be created; the input channels that can be used for objects based on a model; and identification of the primary input channel for objects based on a model.
The famous Document Type class. This class defines the structure and allowed relationships for document objects created based on the model. In addition to the interface inherited from Bricolage::Biz::Type::Model, it defines relationships to sites.
This class defines contributor types. It inherits from Bricolage::Biz::Type::Model but, as of this writing, does not add to its interface.
This class defines category types. The idea is that categories might need custom fields and elements to define content that's common to the entire category. Bricolage::Biz::Type::Model::Category inherits from Bricolage::Biz::Type::Model but, as of this writing, does not add to its interface.
This class defines address types. Really this is just a name and a description for a type of address, so it doesn't extend its parent classes as of this writing. See Bricolage::Biz::Address for the interface of address objects. Implementation of this class may be deferred.
This class defines placement types. Really this is just a name and a description for a type of placement, so it doesn't extend its parent classes as of this writing. See Bricolage::Biz::Placement for the interface of placement objects. Implementation of this class may be deferred.
This class defines contact types. Really this is just a name and a description for a type of contact, so it doesn't extend its parent classes as of this writing. See Bricolage::Biz::Contact for the interface of contact objects.
This class defineds a type of event. It can largely be ported from Bricolage 1.x's Bric::Util::EventType. The event class is Bricolage::Biz::Event.
$et->log_event($bric_obj, \%attrbutes);
A shortcut to Bricolage::Biz::Event->new. Pass in a the object for
which to log the event and a hash reference of attributes for the event, and a
new event of this type will be logged for the object passed. Note that not all
attributes need to be explicitly passed in order to log a new event. Any that
can be called via a get_ accessor on $obj, where the method combines 'get_'
with an attribute's name, will automatically call that accessor.
See Bricolage::Biz::Event for more information and for details on how it expects arugments to be passed.
Defines Bricolage alerts. Alerts are triggered by the logging of an event, and are sent when the event meets certain criteria defined by Bricolage::Biz::Rule objects. Can largely be ported from Bricolage 1.x's Bric::Util::AlertType.
This class defines rules that an object or event must adhere to in order to trigger a job or an alert. For example, when an event is triggered, it looks up a list of alert types configured for events of its type, and then processes all of the rules for that alert type. Provided that the event or its subject meet the criteria of all the rules (that is, their attributes compare favorably to the values or regular expressions defined for the rule), then the alert will be sent. Event-triggered jobs will come in a later phase (2.2?), but will also use rules.
This functionality can be ported from Bric::Util::AlertType::Parts::Rule in Bricolage 1.x.
Bricolage events. Can most likely be ported from Bricolage 1.x, based on Bricolage::Biz::Type::Event. Events will be logged for objects from within the object libraries (instead of in the UI, as in Bricolage 1.x). It may be that certain methods will be configured to log the events by attaching attributes to them, although this approach will need some exploration. How attributes will work may also take some thought, although I'm okay with the current hash-based design for the time being.
A Bricolage job, essentially an event-triggered or time-triggered set of actions that can be carred out. Jobs are based on Bricolage::Biz::Type::Job objects, which describe the job parameters and the conditions under which they're carried out. Publishes, distributions, and alerts will all be executed as jobs.
Bricolage alerts. These can be sent as part of a job, and are thus event-triggered. They're set up by Bricolage::Biz::Type::Alert configurations. Can largely be ported from 1.x's Bric::Util::Alert.
Alert sent to an individual recipient. Can largely be ported from 1.x's Bric::Util::Alerted.
The classes in this package space will, for the time being, be directly ported from Bricolage 1.x's Bric::Dist namespace. The classes to be ported are:
Bric::Dist::ActionType will become Bricolage::Dist::Type::Action and Bric::Dist::Job will become Bricolage::Biz::Job. Bric::Client::Dist will also be moved, probably to Bricolage::Client::Dist.
This is the abstract base class defining the API for all Bricolage elements. Because container and field elements will have the same interface, they can be treated identically by their consumers.
Note that because element templates have their file names based on the
corrsponding element's key_name attribute, all templates associated with a
given element must be updated and redeployed whenever the key name of the
corresponding element changes.
get_value()
accessor, pass in an input channel object, GUID, or name to get the value for
that input channel.
undef.
undef.
This class is a concrete derived class of Bricolage::Biz::Element. It implements the interface necessary for the branches of the element tree, that is, containers for other elements.
This class is a concrete derived class of Bricolage::Biz::Element. It implements the interface necessary for the leaves of the element tree, that is, individual content fields.
Value objects define the interface for the values of field element objects.
They're defined as a class in order to make it easier to add new types
of values to Bricolage. Think of them as data types. They define an interface
for validating that a raw value meets the definition of a value. For example,
a number must not contain any non-numeric characters. All of the validation
of a raw value must be encapsulated in the validate() method.
Each value object is further defined by a Bricolage::Biz::Type::Value object. This object allows administrators to specify other constraints on a value other than its raw definition. For example, a numeric value might be required to fall withing a range, or a string might be required not to exceed a certain length.
Unlike the interfaces defined by most abstract base classes in Bricolage, value subclasses can be extended. This is to allow for certain metadata to be associated with values of particular types. For example, the File value needs methods to access the location and contents of the file, as well as data about the file, such as its size. Image file values additionally require attributes for height and width. See the subclasses below for further specifics.
A value object can have its class changed, ostensibly without changing its value. This conversion from one value class to another must ensure that as much information as possible be retained through the conversion.
Value objects may overload a number of Perl operators. By default,
double-quoted string context will be overloaded to return the raw value stored
in the value() attribute.
new() method.
Each value subclass must override the validate() method, and may add other
attributes as necessary to provide metadata about a value. This list of
subclasses is intended to make a strong start for Bricolage, but may be
extended indefinitely into the future as new users identify a need for new
data types.
as_string() method.
datetime() method.
file_name,
size, and local_uri, as well as instance methods for retrieving the
contents of the file. The file may be stored in the data store, and a copy
will definitely be stored on the file system, to facilitate downloads (hence
the local_uri attribute).
A site, representing a collection of categories, workflows, output channels, elements, and content. Can largely be based on the implementation of Bric::Biz::Site in Bricolage 1.x. However, it must have at least one associated output channel and at least two associated workflows -- one for documents and one for templates.
Output Channels are collections of templates that output content in a particular format. Typically, a site will have output channels for XHTML, RSS, etc. Largely the same as in Bricolage 1.x [although Dave had some ideas about specifying what templaetes within an OC apply to what elements. Look for more details later as we hash out the ideas].
pre_path set to ``en''
and the uri_format set to ``/categories'', a document in the ``/reviews/books''
category would be published to the URI ``/en/reviews/books'' when output from
this output channel. Useful for dividing a site between, for example,
different languages.
post_path set to ``email'' and the
uri_format set to ``/categories'', a document in the ``/reviews/books''
category would be published to the URI ``/reviews/books/email'' when output from
this output channel.
DEFAULT_FILENAME Bricolage::Util::Config setting.
DEFAULT_FILE_SUFFIX Bricolage::Util::Config setting.
filename attribute, if the slug has a
value.
@INC array or multiple Mason component roots. The Burner object will
know to look for templates in these output channels if it cannot find them in
the main output channel. The search order is the order in which they're
included in the collection.
InputChannels are associated with models (that is, anything that inherits from Bricolage::Biz::Type::Model), and defines different channels for content. This means that every field in an element can contain different data for each input channel. This feature will see the most use for sites that require documents to be published in multiple languages.
For example, If a document type is associated with the input channels ``English'' and ``Spanish'', then the document can be edited in the context of either of those output channels. The document will have the same number of elements and fields--it will be structurally identical accross input channels. The only difference is that the individual fields can contain completely different data (presumably English content in the ``English'' input channel and Spanish content in the ``Spanish'' input channel).
All attributes and methods are inherited from Bricolage::Biz. The name
attribute is unique system-wide.
Categories are hierarchically organized categorizations of content. They're used for both documents and templates. Because categories can contain other categories, together making up a hierarchy similar to a file system's directories, they are inherently strucutred as a tree objects. It may or may not make sense to use special code to efficiently load all of the parents of a category whenever the category itself is loaded.
name. Any character is assumed to be usable in the directory
name, including a space, although OS restrictions may apply.
directory attribute of a category or any of its
parents changes. This attribute is read-only.
path attribute.
This class defines the interface for keywords, which can be associated with documents and categories. Can largely be ported from Bricolage 1.x.
The name attribute inherited from Bricolage::Biz is the actual keyword.
name
name
A desk is an area of responsibility within a workflow. It generally indicates what needs to be done to or is being done to the objects it contains. Examples are ``Edit Desk'', where documents are being edited, ``Copy Desk'', where they're being copy edited, and ``Publish Desk'', where they're ready to be approved for publication. Desks can also be implicitly related to approval processes, in that permissions limit who can access what desks.
Desks must be uniquely named within a workflow, but can have the same name as desks in other workflows. A single desk can also be in more than one workflow, and in this way, objects may be transferred across workflows. This is also useful when there's a single department responsible for editing or approving content in all workflows, such as ``Legal'' or ``Translation''.
All attributes are inherited from Bricolage::Biz.
When an object is added to the desk, its desk_guid attribute will be set to
the desk's GUID, and the object will be added to the desk's object group so that
permissions set to access the objects on the desk can be applied. Note that an
object can be on only one desk at a time; an attempt to add an object to a desk
when it's already on another desk will trigger an exception.
This class defines the interface for Bricolage workflows. A workflow is essentially a collection of desks onto which objects can be placed. There are two different types of workflows: those for documents and those for templates. Each type is represented by a subclass.
Note that the first desk in the collection returned by the desks method and
for which a user has ``CREATE'' permission to its objects will be the desk on
which a newly created document will be placed. Likewise, the first desk for
which a user has ``EDIT'' permission to edit its objects will be the desk on
which an object retrieved from the library will be placed. The cumulative
permissions of all of the desks in a workflow will determine the visibility of
the workflow to a user.
Subclass of Bricolage::Biz::Workflow defining the interface for workflows that manage documents. It adds a method to manage the types of documents that can be created in a given workflow.
All Attributes are inherited from Bricolage::Biz::Workflow.
Subclass of Bricolage::Biz::Workflow defining the interface for workflows that manage templates. It adds a method to manage the types of elements for which templates can be created in a given workflow. No limitations exist on the creation of utility or category templates other than the usual permission checks.
All Attributes are inherited from Bricolage::Biz::Workflow.
This class defines the interface for managing Bricolage objects in workflows. It includes methods for desk checkin and checkout, shelving, and version control. Ideally, it will include the necessary implementation so that other classes don't have to implement its interface, but can add to it via inheritance.
I think that the best approach at this point is to make this interface a decorator for other classes. This will allow us to decorate other classes in the future, should we decded to add workflow management to their interfaces. At this point, only documents and templates will be workflowable. They can probably just inherit from Bricolage::Biz::Decorator::Workflowable as a second parent class in order to get its interface.
Each of these attributes must be implemented by a ``workflowable'' class, and
must also be added to the list of parameters that can be passed to search()
and friends. All attributes are versioned unless otherwise noted.
This number can also be considered the ``deployed version'', as it is only incremented at the time an object is deployed. Thus, if the current version of an object is ``7.3'', the deployed version (the version number at the last time the object was deployed) would be ``7.0''.
This number can also be considered to indicate the ``deployed status'' of an object. For an object containing its latest version data, if it is ``0'', then it means that the current object is the most-recently deployed. If it is any other number, it means that the object has been changed since it was last deployed and may need to be deployed again.
These methods must be implemented by a class that's ``workflowable''. They are designed to facilitate the management of an object in workflow, as well as version control.
desk_guid attribute must be set
before the object is saved.
undef, waiting for the object to be
checked in by a new user.
$bric_obj->move($desk);
Moves an object onto a desk. If a user has an object checked out and a different user attempts to move it, an exception will be thrown.
shelve() will first call checkin() when that user shelves the object. If
a different user attempts to shelve it, an exception will be thrown. If no
user has it checked out, it will simply be removed from workflow.
shelve() except that the deploy date will be set, the major version
number will be incremented and the minor version number set to 0. The only
exception is if the minor version of the object being deployed is already 0,
in which case neither version number nor the deploy date will be changed.
A given implementation of this method will likely do other things, too. For example, Bricolage::Biz::Document will publish the document, and Bricolage::Biz::Template will deploy the template.
$bric_obj->revert($version); $bric_obj->revert($version_guid);
Reverts the object to a previous version. The argument can be either a version
number as returned by the version() attribute (such as ``6.23''), or a
version GUID (since each version of an object has its own version GUID). If a
GUID is passed as an argument, the GUID must be the GUID of a version of the
object, not of some other object. Similarly, if the argument is a version
number, such a pre-existing version number must exist. If either of these
criteria are not met, an exception will be thrown.
An object must be checked out in order to be reverted. Reversion does not change the version number, but simply copies the state of the object from the pre-existing version number. When the object is next checked in, its version number will increment as normal. For example, say we have an object with the version number ``8.6'' and wish to revert to the state as of version ``5.4''. This is how it would work:
$bric_obj->revert('5.4'); # Copy state from v 5.4
$bric_obj->checkin; # Increments version number to 8.7.
A document is an instance of a Bricolage::Biz::Type::Model::Document object. The contents of its associated Bricolage::Biz::Element object adhere to the Bricolage::Biz::Type::Element associated with the document type object. Bricolage::Biz::Document inherits from both Bricolage::Biz and Bricolage::Biz::Decorator::Workflowable, as its instances can move through workflows and are versioned.
All attributes and all associations of a document are versioned, including the
name and description attributes inherited from Bricolage::Biz. The
name and description attributes are also subject to the input channel
associations (meaning that they can have different values for each associated
input channel).
undef subsequent to a successful expiration.
categories().
output_channels().
input_channels().
primary_category_guid attribute. Add or remove categories to
the collection to add or remove them from the document, and reorder them in
the collection to change the order of their association with the document.
primary_output_channel_guid attribute. Add or remove output channels to the
collection to add or remove them from the document, and reorder them in the
collection to change the order of their association with the document.
New output channels must be associated with the Bricolage::Biz::Type::Model::Document object that defines the structure of the document. Existing output channels need not be associated with the document type, since they may have been added in the past when they were associated with the document type.
primary_input_channel_guid
attribute. Add or remove input channels to the collection to add or remove
them from the document, and reorder them in the collection to change the order
of their association with the document.
New input channels must be associated with the Bricolage::Biz::Type::Model::Document object that defines the structure of the document. Existing input channels need not be associated with the document type, since they may have been added in the past when they were associated with the document type.
The values returned for field subelements in the element tree associated with
the element attribute are subject to the input channel. If a value is
sought from a field for a given input channel but has no value for that input
channel, the Element object will search backwards from that input channel
through the collection for the first one that does have a value for the
field. In this way, input channels can inherit the values of the higher
priority input channels. The same applies to the value of the name and
description attributes.
$doc->input_channel_for_output($input_channel); $doc->input_channel_for_output($input_channel_guid); $doc->input_channel_for_output($input_channel_name);
Sets the input channel to be used to retrieve values for the name and
description attributes, as well as all of the text fields in the element
tree stored returned by the element() method. Defaults to the primary input
channel.
my $uri = $doc->get_uri; $uri = $doc->get_uri($output_channel); $uri = $doc->get_uri($output_channel, $category);
Returns a URI for the document. If called without arguments, it will return
the value of the primary_uri attribute.
If passed an output channel object, it will retuurn the URI according to that output channel's URI format using the primary category. The output channel must be associated with the document.
If passed a category object, it will return the URI for the document in that category using the primary output channel's URI format. The category must be associated with the document.
If passed both an output channel object and a category object, it will return the URI for the document in that category using that primary output channel's URI format. The output channel and category must both be associated with the document.
deploy() method will also be called. This method should be
called when a user wishes to burn and distribute a document
immediately. Otherwise, in the event of scheduling a publish for later, the
publish job should call this method when it's time for the publish. The actual
act of publishing can be ported from Bricolage 1.x's Bric::Util::Burner. The
only difference is that disstribution should take place immediately, and not
be scheduled (publishes will be scheduled, rather than distributions).
All files burned to disk by the burn process and all files associated with the document's element and subelements via file fields will be deistributed. Any related documents will also be published, provided the publishing user has PUBLISH permission to those documents.
deploy() method will also be called. Any
publishes or expiration jobs not yet executed will be cancelled. Sets the
expire_date to undef. This method will also be called when the state
attribute is changed to DELETED.
This subclass of Bricolage::Biz::Document changes its interface so that it
aliases another Bricolage::Biz::Document object. The only differences from
other Bricolage::Biz::Document objects are the associated alias_guid and that
the element method returns the element object from the aliased document
with read-only permission to that object.
An abstract base class for Bricolage templates. Templates pull in the contents
of documents and format them for output. This class inherits from both
Bricolage::Biz and Bricolage::Biz::Decorator::Workflowable, as its instances
can move through workflows and are versioned. All attributes of a document are
versioned, including the name and description attributes inherited from
Bricolage::Biz.
Note that the deploy() method inherited from
Bricolage::Biz::Decorator::Workflowable must be overridden to also deploy the
template to the appropriate output channel root, as well as to delete any
previous versions of the template (if it's path attribute has changed).
Element templates are associated with elements. Their name attributes are
the same as for the corresponding element. The corresponding element's
key_name attribute is used in the path and uri attributes for the
file name. Thus the path and uri attributes will be updated by any
changes to the corresponding element's key_name attribute.
Category templates are named for the standard category template name and suffix supplied by the burner class associated with the output channel the category template is in. It is otherwise no different than its base class.
Utility templates can have an artibrary name supplied by the uesr. That name is used for the file name, as well. It is otherwise no different than its base class.
The abstract base class for Bricolage burners. These can largely be ported from Bricolage 1.x.
This is a new burner for Bricolage 2.0. It will use the Bricolage::Biz XML API and chained SAX machines to use XSLT to format documents for output. Or maybe it'll use AxKit? I leave it to Mike Slattery to fill in the holes here.
The Bricolage Mason burner. Can be ported from 1.x.
The Bricolage Template Toolkit burner. Can be ported from 1.x.
The Bricolage HTML::Template burner. Can be ported from 1.x.
This namespace it set aside for Bricolage UI classes.
The Bricolage XHTML widget base class. This class defines the interface for widgets, which are used for the display of field elements as well as to power the UI of Bricolage in general.
This is a concrete subclass of Bricolage::UI::Widget that implements all of the required elements to drive the Bricoalge XHTML UI. This is the default and main UI implementation; others may be added in the future.
help_text and display_size and either value_options or
class_attribute. Outputs a select list and options and allows multiple
options to be selected.
help_text and class_attribute. Allows for objects of a particular
type to be searche for.
This class is the abstract base class for UI callbacks. It inherits from Params::Callback to implement the callback interface. These classes are used by any and all of the UIs, plus the SOAP server, to perform common tasks.
Here are few to start with. Many more will likely follow.
This namespace is set aside for classes that manage servers that Bricolage runs.
The handler for the main Bricolage apache server interface (Mason UI, etc.). It may be subclassed to add the necessary code for various Apache request phases, such as Bricolage::Server::Apache::Cleanup.
The Apache handler for the Bricolage SOAP server. This class should be able to use the standard XML interface and Class::Meta introspection classes defined for all Bricolage business classes to handle them all with a minumum of code. The goal is to have the SOAP server not replicate all of the code that's in the UI callbacks as is the case in 1.x.
The Bricolage distribution server. Can likely be ported from 1.x's Bric::Dist::Handler.
This namespace is set aside for any modules useful for Bricolage client
applications, such as the bric_soap script and the bric_dist_mon script.
Libraries for clients of the Bricolage SOAP server.
Libraries for clients of the Bricolage distribution server.
David Wheeler <david@wheeler.net>
With input, advice, feedback, and contributions from:
Mark Jaroski <mark@geekhive.net>
Scott Lanning <slanning@theworld.com>
Dave Rolsky <autarch@urth.org>
Copyright (c) 2001-2003 the World Health Organization. See Bric::License for complete license terms and conditions.