Krang::Category - a means to access information on categories
use Krang::ClassLoader 'Category';
# construct object
my $category = Krang::Category->new(dir => 'category', # required
parent_id => 1,
site_id => 1);
# 'parent_id' must be present for all categories except '/' 'site_id' # must be present for '/'
# saves object to the DB $category->save();
# getters my $element = $category->element(); my $dir = $category->dir(); my $id = $category->parent_id(); my $parent = $category->parent(); my $id = $category->site_id(); my $site = $category->site(); my $may_see = $category->may_see(); my $may_edit = $category->may_edit();
my $id = $category->category_id(); # undef until after save() my $id = $category->element_id(); # undef until after save() my $url = $category->url(); # undef until after save()
# setters $category->dir( $some_single_level_dirname );
# delete the category from the database $category->delete();
# a hash of search parameters
my %params =
( order_desc => 1, # result ascend unless this flag is set
limit => 5, # return 5 or less category objects
offset => 1, # start counting result from the
# second row
order_by => 'url' # sort on the 'url' field
dir_like => '%bob%', # match categories with dir LIKE '%bob%'
parent_id => 8,
site_id => 9,
url_like => '%fred%' );
# any valid object field can be appended with '_like' to perform a # case-insensitive sub-string match on that field in the database
# returns an array of category objects matching criteria in %params
my @categories = pkg('Category')->find( %params );
Categories serve three purposes in Krang. They serve as a means of dividing a
sites content into distinct areas. Consequently, all content sharing the
property of ``being chiefly about 'X''' should be placed within category 'X'. A
category's dir, such as '/X', translates to both a relative system filepath for
preview and publish output and a URL relative path. For example category '/X'
would map to $site->publish_path() . '/X' as well as 'http://' . $site->url() .
'/X'.
Secondly, categories serve as a data container. The 'element' field of a category object is a Krang::Element wherein arbitrary information about the category may be stored.
Thirdly, once a template object is associated with its element, a category serves to provide a layout container for story content that belongs to it. All of the fields defined in the category's element will be available to this template and may be used to derive category-specific layout behavior.
This module serves as a means of adding, deleting, and accessing these objects.
N.B. Categories must be associated with a site via the 'site_id' constructor arg or a 'parent_id' must be passed.
Access to fields for this object is provided my Krang::MethodMaker. The value of fields can be obtained and set in the following fashion:
$value = $category->field_name(); $category->field_name( $some_value );
The available fields for a category object are:
The object's id in the category table
Element object associated with the given category object belonging to the special element class 'category'
Id in the element table of this object's element
The display name of the category i.e. '/gophers'
Id of this categories parent category, if any. This may be changed to alter the parent of a category. This will result in an error for root categories.
The parent object of the present category if any.
Id in the site table of this object's site
The site object identified by site_id.
The full URL to this category
The preview URL for this category
Returns 1 if the current user has permissions to see the category, 0 otherwise.
Returns 1 if the current user has permissions to edit the category, 0 otherwise.
Constructor for the module that relies on Krang::MethodMaker. Validation of '%params' is performed in init(). The valid fields for the hash are:
Either a 'parent_id' or a 'site_id' must be passed but not both.
If the category being created is a subcategory (e.g. it has a parent_id), the value of dir should be the subdirectory itself, NOT the full directory path - that will be handled internally by Krang::Category.
N.b.: The new() method will throw and exception, Krang::Category::NoEditAccess,
if the current user does not have edit access to the parent category.
delete()
Instance or class method that deletes the given category and its associated element from the database. It returns '1' following a successful deletion.
This method's underlying call to dependent_check() may result in a
Krang::Category::Dependency exception if an object in the system is found that
relies upon the Category in question.
N.B. - If this call attempts to remove a root category (i.e. a category whose 'dir' field eq '/') and the call is not made by Krang::Site, a Krang::Category::RootDeletion exception will be thrown. This behavior exists because the deletion of a root category results in a disabled Site. The user would be unable to add categories to this given Site and to correct this he would have to know that he must add another root category before and subcategories could again be added to the Site. This behavior is preferable to requiring so comprehensive an understanding of the API by the user :).
This method will throw a Krang::Category::NoEditAccess exception if a user without edit access tries to delete the category.
dependent_check()
Class or instance method that should be called before attempting to delete the given category object. If dependents are found a Krang::Category::Dependent exception is thrown; otherwise, 0 is returned.
Krang::Category::Dependent exceptions have one field 'dependents' that contains a hashref of the classnames and ids of the objects which depend upon the given category object. You might want to handle the exception thusly:
eval {$category->dependent_check()};
if ($@ and $@->isa('Krang::Category::Dependent')) {
my $dependents = $@->dependents();
$dependents = join("\n\t", map{"$_: [" .
join(",", @{$dependents->{$_}}) .
"]"} keys %$dependents);
croak("The following object classes and ids rely upon this " .
"category:\n\t$dependents);
} else {
die $@;
}
duplicate_check()
This method checks the database to see if an existing category or story already has the same 'url' as the object in memory. If a duplicate is found, a Krang::Category::DuplicateURL exception is thrown; otherwise, 0 is returned.
Krang::Category::DuplicateURL exceptions have a single nonempty field - either 'category_id' or 'story_id' - that indicates the id of the clashing object
eval {$self->duplicate_check()};
if ($@ and $@->isa('Krang::Category::DuplicateURL')) {
if ($@->story_id) {
croak("The 'url' of this category duplicates that of story id: " .
$@->story_id\n");
} elsif {
croak("The 'url' of this category duplicates that of category id: " .
$@->category_id\n");
}
}
reserved_check()
This method checks to see if URL of this category clashes with a reserved
URL as specified by the ReservedURLs configuration directive. If it
conflicts, then a Krang::Category::ReservedURL exception will be thrown.
eval { $self->reserved_check() };
if ($@ and $@->isa('Krang::Category::ReservedURL')) {
croak("The 'url' of this category is reserved");
}
ancestors()
Will return array of Krang::Category objects or category_ids of parents and parents of parents etc
root_category()
Will return the root category of $category or $category itself if it is the root category.
descendants()
Returns a list of Krang::Category objects or category_ids of all descendants of $category;
children()
Returns array of Krang::Category objects or category_ids of immediate childen. Convenience method to find().
Class method that returns an array of category objects, category ids, or a count. Case-insensitive sub-string matching can be performed on any valid field by passing an argument like: ``fieldname_like => '%$string%''' (Note: '%' characters must surround the sub-string). The valid search fields are:
Additional criteria which affect the search results are:
If this argument is specified, the method will return a count of the categories matching the other search criteria provided.
Returns only category ids for the results found in the DB, not objects.
Specify this argument to determine the maximum amount of category objects or category ids to be returned.
Sets the offset from the first row of the results to return.
Specify the field by means of which the results will be sorted. By default results are sorted with the 'category_id' field.
Set this flag to '1' to sort results relative to the 'order_by' field in descending order, by default results sort in ascending order
Will ignore user in ENV if set to 1.
The method croaks if an invalid search criteria is provided or if both the 'count' and 'ids_only' options are specified.
element (readonly)
The element for this category.
save()
Saves the contents of the category object in memory to the database. Both
'category_id' and 'url' will be defined if the call is successful. If the
'dir' field has changed since the last save, the 'url' field will be
reconstructed and update_child_url() is called to update the urls underneath
the present object.
The method croaks if the save would result in a duplicate category object (i.e. if the object has the 'dir' as another object). It also croaks if its database query affects no rows in the database.
This method will throw a Krang::Category::NoEditAccess exception if a user without edit access tries to save the category.
update_child_urls()
Instance method that will search through the category, media, story, and template tables and replaces all occurrences of the category's old dir with the new one.
@linked_stories = $category->linked_stories
Returns a list of stories linked to from this category. These will be Krang::Story objects. If no stories are linked, returns an empty list. This list will not contain any duplicate stories, even if a story is linked more than once.
@linked_media = $category->linked_media
Returns a list of media linked to from this category. These will be Krang::Media objects. If no media are linked, returns an empty list. This list will not contain any duplicate media, even if a media object is linked more than once.
$category->serialize_xml(writer => $writer, set => $set)
Serialize as XML. See Krang::DataSet for details.
$category = Krang::Category->deserialize_xml(xml => $xml, set => $set, no_update => 0, skip_update => 0)
Deserialize XML. See Krang::DataSet for details.
If an incoming category has the same URL as an existing category then an update will occur.
$data = Storable::freeze($category)
Serialize a category. Krang::Category implements STORABLE_freeze() to
ensure this works correctly.
$category = Storable::thaw($data)
Deserialize a frozen story. Krang::Category implements STORABLE_thaw()
to ensure this works correctly.
$category->can_copy_test(dst_category => $destination_category)
Test if a recursive copy of the $category's children to the
$destination_category would be possible. The actual copy operation is
done by copy(). can_copy_test() should always be called before
copy(), and it should be called with the same argument and the same
options to make sure the copy will succeed.
The following exceptions can occur:
Throwns if the URL of a would-be-created category is occupied by some story and we cannot check out this story (if we can, we can turn the story into a category index, but this is done by copy()).
Can be thrown if an asset's destination category already exists and we don't have edit access to this category. This can only happen when copying to non-leaf-categories.
Thrown if one of the would-be-created assets already exists unless the option 'overwrite' is specified.
Thrown if the copy will result in infinit recursion. This would happen if you tried to copy a parent directory into it's a child.
Options
Also test wether copying stories living below $category and its children
would succeed.
Also test wether copying media living below $category and its children
would succeed.
Also test wether copying templates living below $category and its children
would succeed.
This option modifies the test behavior of this method. No
Krang::Category::CopyAssetConflict will be thrown even if the URL of
a would-be-created asset is already occupied by some other asset living
below the destination category.
$category->copy(dst_category => $destination_category)
Copies the children of $category to the specified destination
category. Make sure to call $category->can_copy_test() with the same
argument and the same options before calling this method!
Options:
Concerning the following three options note that stories/media/templates whose would-be-URL is occupied by some existing asset will not be copied, but silently skipped. But see the option 'overwrite'.
Also recursively copy the stories living below $category to their corresponding destination category.
Also recursively copy the media living below $category to their corresponding destination category.
Also recursively copy the templates living below $category to their corresponding destination category.
Normally an asset whose copy destination URL is occupied by an existing asset will not be copied. Setting 'overwrite' to true modifies this behavior: The conflicting asset will be moved to the trashbin to make place for the copy.
Note on calling can_copy_test() and copy() with overwrite set to false
In the case of an asset conflict, can_copy_test() throws a
Krang::Category::CopyAssetConflict exception, while copy() simply
skips the conflicting assets. Catching the exception, then, allows to
prompt the user: Does he want to cancel the whole copy, or is it ok to
just copy the non-conflicting assets. This is how runmode
Krang::CGI::Category->execute_copy() uses these two methods.
@assets = $category->asset_names()
@assets = pkg('Category')->asset_names()
Convenience method for operations on assets. It returns a list of hashrefs representing Krang asset specifications.
The 'type' key of each of these hashrefs is just the lower-cased moniker of those assets, i.e. 'story', 'media' and 'template'.
The 'meth' key maps to the asset's 'file name' method: It's 'slug' for Story, 'filename' for Media and Template.
Krang, Krang::DB, Krang::Element