NAME

Bricolage Multi-Site Feature Implementation Documentation


VITALS

Version
$Revision: 1.14 $

Date
$Date: 2003/03/22 15:57:48 $

Status
In progress

Target Version
1.8


DESCRIPTION

This document describes everything done to implement the multi-site feature in Bricolage.


API CHANGES

Bric::Biz::Site

Bric::Biz::Site was implemented similarly to the other Bricolage business objects. It inherits from Bric, uses Bric::register_fields() to register its instance attributes and to create accessors, and stores its data in the database.

Interface

The interface for Bric::Biz::Site is nearly exactly as it was described in MultiSite::TechnicalSpec. The only difference is the addition of a couple of private instance methods, get_asset_grp(), and list_priv_grps(), and the removal of the user_id parameter to list() and friends. See Bric::Biz::Site for a complete description of the API.

Note that, as described in MultiSite::TechnicalSpec, the IDs for all sites correspond to a single secret group in which the site is actually a member. This approach makes it very easy to use the site ID as a group ID as well, in those classes that need to include a site ID in their grp_ids attributes for permission-checking purposes. Good examples of this use include Bric::Biz::Asset and it subclasses.

The private get_asset_grp() method returns the secret asset group that corresponds to a site object ID. I've included it for completeness, but left it private for now, as there has not been a public need for it as of yet.

The private list_priv_grps() method returns the four secret user groups created by every site object. I've included it for completeness, but left it private for now, as there has not been a public need for it as of yet (although it seems likely that it will later be needed for displaying user groups in the site profile). The four groups are each named for the site plus a permission, such as ``Default Site READ Users'', and has a Bric::Util::Priv permission object with the appropriate permission granted to the asset group represented by the site ID and returned by get_asset_grp(). This implementation is admittedly a bit hackish, but was the simplest approach for providing the ability to grant users to access assets within a site while exploiting the existing group-based permission system and thus avoiding creating a totally independent permissions system just for sites.

The user_id parameter had to be removed from list() because it proved much to complicated to get the permissions checking correct in the SQL query, and because it didn't allow members of the ``Global Admins'' group to short-circuit permissions checking.

SQL

The Bric::Biz::Site data is stored in the database in the ``site'' table. The columns correspond to the attribute names. The ``id'' column has a primary key index, and the ``name'' and ``domain_name'' columns will each have a UNIQUE index. There is no sequence for this table, since sites use IDs grabbed from groups (and thus from the ``grp'' table's sequence). Thus the ``site'' table's ``id'' column also has a foreign key constraint linking it to the ``grp'' table's ID column. See inst/upgrade/1.7.0/site.pl for all of the relevant SQL.

Data

By default, Bricolage now contains one default site, named ``Default''. Its ID is 100. It is associated with a new permanent secret asset group, which has the same ID. It is further associated with four new permanent secret groups with the IDs 200-203. Each of those, in turn, as a permission tying it to the site's asset group (id 100), granting the appropriate permission for each group. The new permission IDs are 43-46.

A new entry has been added to the ``class'' table in the database for the new Bric::Biz::Site class. It's key_name is ``site''.

See inst/upgrade/1.7.0/site.pl for all of the relevant SQL.

Tests

A comprehensive set of tests was added to give the new Bric::Biz::Site API a thorough working-over. The tests are all in Bric::Biz::Site::Test and Bric::Biz::Site::DevTest in the t directory. Note that these tests test the attributes, groups, and permissions of both the default site and sites they create using the API, in order to ensure parity between the default site and those created by the API (and I did catch and fix differences because of it!). I expect that these tests will become a model for the comprehensive testing of a Bricolage class, and their approach will be borrowed to create tests for all new Bricolage classes from here on in. Existing classes, too, will gradually gain more comprehensive tests modeled on these tests.

Documentation

The Bric::Biz::Site interface is completely documented in POD. The POD is in a new style that varies considerable from the POD of existing Bricolage classes. This, too, will become the standard for new Bricolage classes from here on in. Existing classes will gradually have their POD styles updated to match. A thorough description of this POD style will soon be added to Bric::Hacker.

Bric::Util::Grp::Site

This new subclass of Bric::Util::Grp manages groups of Bric::Biz::Site objects. Site groups will not be secret by default, and the Bric::Util::Grp::Site class references the new Bric::Util::Class object ID for the Bric::Biz::Site class and the Bric::Util::Class object ID for for the Bric::Util::Grp::Site class itself.

Interface

The interface for Bric::Util::Grp::Site is directly inherited from Bric::Util::Grp.

SQL

A new table, ``site_member'', was added to the database, as was a sequence for it, ``seq_site_member'', and the appropriate foreign key indexes. See inst/upgrade/1.7.0/site.pl for all of the relevant SQL.

Data

A new default group, ``All Sites'', has been added, and all Bric::Biz::Site objects will be members of this group.

A new entry has been added to the ``class'' table in the database for the new Bric::Util::Grp::Site class. It's key_name is ``site_grp''.

See inst/upgrade/1.7.0/site.pl for all of the relevant SQL.

Bric::Util::Grp

A few changes were made to Bric::Util::Grp in order to facilitate the new dynamically-created groups that need to be both secret and permanent. First, permanent and secret parameters to new() are now explicitly supported. And second, the long-standing conflict between the class method get_secret() method and the instance method get_secret() (or lack thereof) had been eliminated by the addition of the instance method is_secret(). Now get_secret() returns the default secret value for new group objects, while is_secret() returns the value for an actual group instance. The TODO test in Bric::Util::Grp::DevTest was thus changed to a normal test.

Bric::Biz::Person::User

There was only one very simple change to Bric::Biz::Person::User: its get_grps() method was altered so that it would return all user groups, including secret groups. This is so that it will return the secret groups created by Bric::Biz::Site, so that they may be displayed in the user profile and users thus easily associated with them.

Bric::Util::EventType

No changes were made to the Bric::Util::EventType class, but new event types were added to the database, ``site_new'', ``site_save'', and ``site_deact''. See inst/upgrade/1.7.0/site_events.pl for the SQL that creates the new events.

Bric::Util::Fault

Bric::Util::Fault had a couple new subclasses added to it.

Bric::Util::Fault::Error and its subclasses are data error exceptions. They're designed to be thrown now when there's a fatal error, but when there's a non-fatal error that the user can respond to. As such, it has a new attribute and method, maketext, which takes as its argument an array reference of arguments suitable for passing to Bric::Util::Langage->maketext. A first subclass has been added, Bric::Util::Fault::Error::NotUnique. Bric::Biz::Site throws this exception when it notes that a value passed to its set_name() or set_domain_name() methods is already in use by another site in the database -- in other words, when the value is not unique. Bric::Biz::Site throws the exception like this:

  throw_not_unique error    => "Value of $disp cannot be empty",
                   maketext => ["Value of [_1] cannot be empty", $disp]
    unless $value;

Note the use of the array reference for the maketext parameter. This allows the UI callbacks to pass the localization arguments off to Bric::Util::Language.

Bric::Util::Language

Bric::Util::Language now overrides the parent maketext() method so that it can detect whether its arguments are passed as an array reference or as a list and do the right thing. This is to support the new Bric::Util::Fault::Error class' array reference maketext attribute:

  eval {
      ...
  };
  if (my $err = $@) {
      rethrow_exception($err) unless isa_bric_exception($err, 'Error');
      add_msg($lang->maketext($err->maketext));
  }

Bric::App::Cache

Interface

Two new methods were added to Bric::App::Cache, get_user_cx() and set_user_cx(). These are used to get and set the site context for a given user. They were added Bric::App::Cache because they needed to persist across sessions and because, API-wise, sites are not too closely tied to users, so storing the data in the database doesn't make much sense.

Tests

A full suite of tests was written for Bric::App::Cache, as well. None existed before now.

Bric::Biz::Category

Interface

The interface for Bric::Biz::Category follows the changes called for in MultiSite::TechnicalSpec.

Removed ROOT_CATEGORY_ID constant and replaced it with methods site_root_category and site_root_category_id. One returns the root category object for a given site and the other just returns the category ID for a given site. If called as a class method the site ID or site object must be passed as an argument. If called as an instance method the site_id will be pulled from the object itself.

One final method has been added, is_root_category which can be used to determine if the object this is called against is a root category or not. The criteria for this right now is that its parent_id is the ID of the master root category. The master root category has ID 0 and all site root categories list it as their parent. Nothing should ever use the master root category directory as its just there to allow the parent_id field to remain NOT NULL.

SQL

The Bric::Biz::Category SQL follows the changes called for in MultiSite::TechnicalSpec.

See inst/upgrade/1.7.0/site_category.pl for all of the relevant SQL.

Data

The site__id for existing categories has been set to the default site with ID 100.

Added a second category to be the root category for the default site. The original category will be kept as a master category. It has an ID of 0 and its uri field set to an empty string, ''. SQL for other classes refering to the old root category have been updated. To point at the new root category set up for the default site.

See inst/upgrade/1.7.0/site_category.pl for all of the relevant SQL.

Tests

The tests are all in Bric::Biz::Category::Test and Bric::Biz::Category::DevTest in the t directory.

Documentation

The Bric::Biz::Category interface is completely documented in POD of this package.

Bric::Biz::OutputChannel

Interface

The interface for Bric::Biz::OutputChannel follows the changes called for in MultiSite::TechnicalSpec.

SQL

The Bric::Biz::OutputChannel SQL follows the changes called for in MultiSite::TechnicalSpec.

See inst/upgrade/1.7.0/site_output_channel.pl for all of the relevant SQL.

Data

The site__id for existing output channels has been set to the default site with ID 100.

The protocol field has been left null.

See inst/upgrade/1.7.0/site_output_channel.pl for all of the relevant SQL.

Tests

The tests are all in Bric::Biz::OutputChannel::Test and Bric::Biz::OutputChannel::DevTest in the t directory.

Documentation

The Bric::Biz::OutputChannel interface is completely documented in POD of this package.

Bric::Biz::Asset with subclasses

Interface

The interface for Bric::Biz::Asset follows the changes called for in MultiSite::TechnicalSpec regarding key_name.

The Asset::Business types have been changed to require a site_id for the selected asset. A new attribute alias_id has been added so an asset can be an alias to another asset so you can link assets between multiple sites.

SQL

The Bric::Biz::Asset SQL follows the changes called for in MultiSite::TechnicalSpec regarding key_name.

See inst/upgrade/1.7.0/site_asset.pl for all of the relevant SQL.

A new column, site__id was added with a foreign key to the site table. For the Story and Media type, a new column alias_id was added with a foreign key to itself.

Data

All current stories are transferred to the default site.

Bric::Biz::AssetType

Interface

The interface for Bric::Biz::AssetType follows the changes called for in MultiSite::TechnicalSpec regarding key_name.

The interface now contains the following added functions

add_sites
Takes an array ref of site ids or site objects.

add_site
Takes a single site id or site object, this is not per spec but mimics the interface from add_output_channel(s).

get_sites
Returns all sites associated with the AssetType

remove_sites
Takes an array of site ids or site objects and marks them for disassociation from the AssetType object. This is renamed remove_sites instead of delete_sites since it doesn't actually delete anything.

The following interfaces has been changed

new
Can no longer take primary_oc_id as a parameter to new. Didn't know which site to attribute the primary_oc_id too.

get_primary_oc_id()
Is now changed so it requires a site id or site object as a parameter.

set_primary_oc_id(id)
Is now change so it requires a site id or site object as a second paramaeter.

list
Now supports getting a list of elements for a specified site.

A new collection has been added to manage the relationship between sites and elements. The class is named Bric::Util::Coll::Site and is used internally by the AssetType class.

SQL

The Bric::Biz::AssetType SQL follows the changes called for in MultiSite::TechnicalSpec regarding key_name.

See inst/upgrade/1.7.0/asset_type_key_name.pl for all of the relevant SQL.

For element to site mapping a new table was created called element__site as specified in MultiSite::TechnicalSpec, however the id/pik was not necessary to maintain integrity and the element__output_channel was not needed to be redone since it contains information about both the site__id and the element__id.

The upgrade script is inst/upgrate/1.7.0/site_element.pl.

Data

The key_name field has been set to the name field after it has been lowercased and all characters that are not a-z converted into underscores ('_').

See inst/upgrade/1.7.0/asset_type_key_name.pl for all of the relevant SQL.

The selected output channels and primary_oc_id information is moved to the element__site table by the upgrade inst/upgrate/1.7.0/site_element.pl script. It is per default associated with the default site with an id of 100.

Tests

The tests are all in Bric::Biz::AssetType::Test and Bric::Biz::AssetType::DevTest in the t directory.

Documentation

The Bric::Biz::AssetType interface is completely documented in POD of this package.

Bric::Biz::AssetType::Parts::Data

Interface

The interface for Bric::Biz::AssetType::Parts::Data follows the changes called for in MultiSite::TechnicalSpec.

SQL

The Bric::Biz::AssetType::Parts::Data SQL follows the changes called for in MultiSite::TechnicalSpec.

See inst/upgrade/1.7.0/asset_type_data_key_name.pl for all of the relevant SQL.

Data

The 'name' field is renamed to 'key_name', its data has been lowercased and all characters that are not a-z converted to underscores ('_'). Data that would be the 'name' field exist already in the 'disp' key of the metadata for the attribute 'html_info'

See inst/upgrade/1.7.0/asset_type_data_key_name.pl for all of the relevant SQL.

Tests

The tests are all in Bric::Biz::AssetType::Parts::Data::Test and Bric::Biz::AssetType::Parts::Data::DevTest in the t directory.

Documentation

The Bric::Biz::AssetType::Parts::Data interface is completely documented in POD of this package.

Bric::Biz::Asset::Business::Parts::Tile::Container

Interface

The interface for Bric::Biz::Asset::Business::Parts::Tile::Container follows the changes called for in MultiSite::TechnicalSpec.

SQL

The Bric::Biz::Asset::Business::Parts::Tile::Container SQL follows the changes called for in MultiSite::TechnicalSpec.

See inst/upgrade/1.7.0/container_tile_key_name.pl for all of the relevant SQL.

Data

The name field is renamed to 'key_name', its data has been lowercased and all characters that are not a-z converted to underscores ('_').

See inst/upgrade/1.7.0/container_tile_key_name.pl for all of the relevant SQL.

Tests

The tests are all in Bric::Biz::Asset::Business::Parts::Tile::Container::Test and Bric::Biz::Asset::Business::Parts::Tile::Container::DevTest in the t directory.

Documentation

The Bric::Biz::Asset::Business::Parts::Tile::Container interface is completely documented in POD of this package.

Bric::Biz::Asset::Business::Parts::Tile::Data

Interface

The interface for Bric::Biz::Asset::Business::Parts::Tile::Data follows the changes called for in MultiSite::TechnicalSpec.

SQL

The Bric::Biz::Asset::Business::Parts::Tile::Data SQL follows the changes called for in MultiSite::TechnicalSpec.

See inst/upgrade/1.7.0/data_tile_key_name.pl for all of the relevant SQL.

Data

The name field is renamed to 'key_name', its data has been lowercased and all characters that are not a-z converted to underscores ('_').

See inst/upgrade/1.7.0/data_tile_key_name.pl for all of the relevant SQL.

Tests

The tests are all in Bric::Biz::Asset::Business::Parts::Tile::Data::Test and Bric::Biz::Asset::Business::Parts::Tile::Data::DevTest in the t directory.

Documentation

The Bric::Biz::Asset::Business::Parts::Tile::Data interface is completely documented in POD of this package.

Bric::Dest::ServerType

Interface

The interface for Bric::Dist::ServerType follows the changes called for in MultiSite::TechnicalSpec.

SQL

The Bric::Dist::ServerType SQL follows the changes called for in MultiSite::TechnicalSpec.

See inst/upgrade/1.7.0/site_server_type.pl for all of the relevant SQL updates.

Data

The 'site__id' field has been addded to the server_type table and populated with ID 100, the default site.

See inst/upgrade/1.7.0/data_tile_key_name.pl for all of the relevant SQL.

Tests

The tests are all in Bric::Dist::ServerType::Test and Bric::Dist::ServerType::DevTest in the t directory.

Documentation

The Bric::Dist::ServerType interface is completely documented in POD of this package.

Bric::Biz::Workflow

Interface

A new instance attribute site_id was added, new set_site_id and get_site_id methods where added.

Methods new, list and lookup get support for new parameter site_id. A workflow is no longer unique only on it's name.

SQL

The Bric::Biz::Workflow SQL follows the changes called for in MultiSite::TechnicalSpec.

New column site__id added the workflow table, unique index changed to be site__id + LOWER(name) but using a custom 'lower_text_num' function.

See inst/upgrade/1.7.0/workflow_site.pl for all of the relevant SQL.

Data

The site__id for existing workflows has been set to the default site with ID 100.

Tests

Tests have been added to Bric::Biz::Workflow::DevTest in the t directory, for all new interfaces.

Documentation

The Bric::Biz::Workflow interface is completely documented in POD of this package.


UI CHANGES

The Header

For those with access to more than one site, a new ``Site Context'' select list has been added to the top of every page. It was placed in all pages, rather than just on ``My Workspace'', because the ``Context'' bar just below the title tab seemed the most appropriate place for it. It's also a lot more convenient for users. The list is managed by a new widget, comp/widgets/site_context/site_context.mc, which has an associated callback to change the context of a user when a new one is selected. The currently-selected context for each user is cached by Bric::App::Cache so that it will persist across sessions.

A special ``All Sites'' option may be enabled by a new bricolage.conf configuration directive, ALLOW_ALL_SITES_CX. When enabled, an additional option, ``All Sites'', is added to the Site Context menu. When selected, this option will allow the user to see the workflows for all sites she's associated with; the ALLOW_ALL_SITES_CX directive is off by default.

The list of sites is cached by Bric::App::Cache, and the list of sites for a given user is stored in her session. Changes made to any site, user, user group, or site group object lead the list of sites in the cache to be expired and reloaded from the database. This approach optimizes the performance of the Site Context list while allowing changes to group permissions to affect all users immediately. The approach taken is similar to that historically used for displaying workflows in the side navigation bar.

Site Manager

The new site manager code was added to comp/admin/manager/dhandler. It lists the name and domain_name properties of site objects, refers select actions to comp/admin/profile/site/dhandler, and delete actions to comp/widgets/site/callback.mc. The latter is a new callback, used instead of listManager's default deactivate callback to ensure that the list of sites in the cache gets expired and will be reloaded by the sideNav component on the next request.

Site Profile

This brand new profile is designed to manage site objects. It's actually relatively straight-forward, and simpler than that described in the technical spec. It does, of course, have an ``Information'' section, in which its basic properties (Name, Description, Domain Name) can be edited. It also includes a doubleList Manger section so that a site can be associated with site groups. (Note that I've created a ToDo item for moving this functionality out of the User, Category, and Site Profiles and into a widget to handle group association for all major administration profiles).

The third section with the user group associations I've decided to leave out for now. The reason for this is that most of the permissions granted to access the assets in a site by users will be managed in the user profile, where the secret READ, EDIT, CREATE, and DENY groups for each site will be displayed and a user can be associated. While it might be convenient to also do some permissions assignment in the site profile itself, such is not crucial for getting this feature completed. Furthermore, this functionality would also be welcome in the element, category, and workflow profiles. It makes sense, then, to add this functionality to all of those profiles later as a widget. Thus, I've decided to leave it for later, when the site functionality is actually fully functional and we have time to add bells and whistles such as this.

The callback for the site profile is fairly simple, since it needs to handle only a few properties of a site. Furthermore, since the Bric::Biz::Site class handles data validation itself (such as ensuring that the Name and Domain Name attributes are unique), the callback doesn't have to do any validation itself. Rather, it merely takes care of the assignments inside an eval{} block, and does the right thing with any exceptions it catches. If it catches a Bric::Util::Fault::Error exception, it uses the $lang global to localize the exception object's maketext attribute and passes the result to add_msg(). It then returns the object, where the user will see her localized error message and make any changes.

Otherwise, the callback continues to process the group associations (using code borrowed form the user profile callback), expires the list of sites in the cache, logs the appropriate event and then returns the user to the site manager.

Delete actions are handled a bit differently. A site to be deleted is deactivated and saved, the list of sites in the cache is cleared, an event is logged, and the user is returned to the site manager.

Group Profile

The group profile was modified so that by default it passes an active attribute to class' list() methods. This is so that classes like Bric::Biz::Site that need an explicit active parameter (or else return active and inactive objects) will return only active objects.

User Profile

The user profile was modified so that its call to Bric::Util::Grp::User->list passes a true all parameter. This is so that secret groups created by Bric::Biz::Site will be properly displayed. This seems to be all right, as no other class appears to create secret user groups, and none of the default user groups are secret.


AUTHOR

David Wheeler <david@kineticode.com>


SEE ALSO

MultiSite
This is the main document for the element revision project, pointing to all documentation relevant to the project.

MultiSite::FunctionalSpec
The functional specification describes the multi-site feature, what it will look like, and how it will work.

MultiSite::TechnicalSpec
The technical specification outlines how the multi-site feature will actually be implemented in Bricolage.