Krang::ElementClass - base class for Krang element classes
package ElementSet::element_name; use Krang::ClassLoader base => 'ElementClass';
# override new() to setup element class parameters
sub new {
my $pkg = shift;
my %opt = (name => "element_name", @_);
return $pkg->SUPER::new(%opt);
}
1;
This class serves as the base class for all Krang Element classes.
Element classes are created by inheriting from this class or one of
its sub-classes (Krang::ElementClass::SelectBox, for example).
Sub-classes must override several methods and setup the required
'name' attribute by overriding new().
For a higher-level overview of the Krang element system, see docs/element_system.pod.
Krang::ElementClass objects have the following attributes, available through the standard Krang::MethodMaker accessors. Sub-classes may add new attributes as needed to implement their functionality, but these will always be available.
The unique name of the element. Use this instead of ref($obj) to
determine the indentifier for the element class. Concrete
(non-abstract) descendents of Krang::ElementClass must define a value
for this attribute.
The name that users will see in the story UI when editing this
element. Defaults to ucfirst()ing each word in name after
splitting on underscores. Thus, a class with the name
``horizontal_line'' has the default display_name ``Horizontal Line''.
The minimum number of times the element can appear within another element. If this is greater than 0 then instances of this element will be created when the containing object is created. Defaults to 0.
The maximum number of times the element can appear. Defaults to 0, meaning any number is allowed.
If this attribute is set true then the element will not be available for use within the UI. However, it will still function if used by existing stories or categories. Defaults to 0.
If set as a subroutine reference, then it will be called and the value returned will be used.
If set to 1 or to the string 'textarea' then this element will be available for bulk_editing in one big textarea. Classes marked for bulk editing must not have a max value set. See Krang::BulkEdit::Textarea.
If set to 'xinha', the textarea will be replaced with a Xinha editor. See also 'bulk_edit_tag'.
If one or more siblings of the elementclass also have this attribute set to 'xinha', the bulk edit drop down menu of these elements' parent will have an additional entry 'All WYSIWYG Elements'. This will put the data of all corresponding elements into the Xinha edit area, using the 'bulk_edit_tag' to identify the data's elementclass.
If 'bulk_edit' is set to 'xinha', this attribute might be set to any allowed block-level HTMLElement, including
p, ul, ol, h1, h2, h3, h4, h5, h6, hr, table, address, blockquote pre
For a discussion of what is allowed see the documentation for the
class method html_scrubber in Krang::BulkEdit::Xinha::Config.
Besides of formatting the element's data when displayed in Xinha, this tag marks the data as belonging to its elementclass. Block elements that do not have their own elementclass will be put in an elementclass having the bulk_edit_tag 'p'. If no such class exists, a class having its 'name' attribute set to 'paragraph' will be used. If none is found, Krang croaks.
This attribute may be used in conjunction with Xinha-based bulk editing (see bulk_edit above). It takes a coderef allowing to modify the element's data just before passing it to Xinha. The coderef receives the element object as its sole argument (see the example below).
B<Example:>
(Prepending 'Correction: ' before to element's data when bulk editing it)
pkg('ElementClass::Textarea')->new(
name => 'correction',
cols => 30,
rows => 4,
bulk_edit => 'xinha',
bulk_edit_tag => 'pre',
before_bulk_edit => sub {
my (%arg) = @_;
my $element = $arg{element};
return "Correction: " . $element->data;
},
This kind of ``before edit modification'' may be used together with the
attribute before_bulk_save.
This attribute takes a coderef as its value which is passed the data about to be saved in the element's data slot. It may be used to modify the data coming from Xinha's bulk edit area just before saving it.
B<Example:>
(Stripping 'Correction: ' before saving the element's data
pkg('ElementClass::Textarea')->new(
name => 'correction',
cols => 30,
rows => 4,
bulk_edit => 'xinha',
bulk_edit_tag => 'pre',
before_bulk_save => sub {
my (%arg) = @_;
my $data = $arg{data};
$data =~ s/^Correction: //;
return $data;
},
This kind of ``before save modification'' may be used together with the
attribute before_bulk_edit.
If set to 1 then the user must fill in a value for this element in the
UI. Sub-classes should use this flag within their validate() methods.
See validate() for more details. Defaults to 0.
If set to 0 then the UI will not allow the user to reorder the element. This is generally only useful for elements with min and max set to 1. Defaults to 1.
If set to 0 then the UI will not allow the element to be deleted. Defaults to 1.
A default value for elements of this class. Will be loaded into their data slot on creation, so this must be a valid value for the element.
If set to 1 then the contents of this element will be indexed. Defaults to 0. See index_data() for more details.
If set to 1 then the contents of this element will be thawed only when
needed. This means that thaw_data() is called when data() is
first called on the object, not when the object is loaded. Defaults
to 0.
This attribute is set with an array of the available sub-elements for the container element. These elements may be one of two types:
A string indicating the names of potential sub-elements. For example,
paragraph would cause a search through defined element sets for a
class ending in ::paragraph.
An object belonging to classes descending from Krang::ElementClass. This allows you to instantiate element classes dynamically.
For example:
$class->children([
"paragraph",
"image",
pkg('ElementClass::Text')->new(name => "header"),
LA::image_group->new(location => "bottom"),
]);
Although both scalars and objects are valid inputs to children(), only fully constructed objects will be returned from the accessor.
The following methods are available on all Krang::ElementClass objects, and should not be overriden in sub-classes.
$child = $class->child($name)
Finds and returns a child by name. This is faster than calling
children() and looping through the results calling name() if all
you need is a particular child class.
$bool = $class->is_container
Returns true if the element class has children.
The following methods are available on all Krang::ElementClass objects. All of these methods may be overridden in child classes to specialize the behavior of an element class.
$html = $class->input_form(element => $element, query => $query)
This call is used to display an HTML form element for data entry in the frontend. It must return the HTML text to be used. The default implementation returns a hidden field.
@names = $class->param_names(element => $element)
Returns the CGI parameter names that will be used for the element.
The default implementation returns a single parameter name of
$element->xpath(). If you create a sub-class with multiple form
inputs you will need to override this method.
$data = $class->bulk_edit_data(element => $element)
This method may be used to filter a bulk-edited element's data just before concatenating it with its siblings' data. It must work for all classes that set bulk_edit to 1, 'textarea' or 'xinha'. The default implementation just returns $element->data.
@data = $class->bulk_edit_filter(data => \@data)
Given an array of text blocks, return an array of scalars suitable for passing individually to data(). The data passed to this method comes from the bulk edit text field and has been pre-split on the chosen separator. This method must transform this data into a format suitable for data(). The default implementation returns the data as-is.
$html = $class->view_data(element => $element)
Called to return the HTML to use in the element view screen. This is
a static representation of the data in an element. The default
implementation returns the contents of $element->data with all
HTML tags escaped for display.
$parent->bulk_save_change(class => $child_class, data => $data)
This method is called in Krang::BulkEdit::Xinha when bulk saving an element just before adding a new child. It is passed the elementclass (not its name) the new child would normally belong to, the data it would receive and the parent element. Based on these parameters the name of a sibling class may be calculated, and the data modified. If this method is overridden, it must return a list containing the new class and the new data, whether or not they match the old values. The default implementation returns them unchanged.
($bool, $msg) = $class->validate(element => $element, query => $query)
Given the CGI.pm query object from a form submission, this call must return true if the input is valid for the element and false if not. If false, an error message should be returned as well, describing the error.
The default implementation respects the required attribute but
otherwise does no checking of the input data.
($bool, $msg) = $class->validate_children(element => $element, query => $query)
Given the CGI.pm query object from a form submission, this call must return true if the input is valid for the elements children, taken collectively and false if not. If false, an error message should be returned as well, describing the error. This method is only called if all the children return (1) from their validate methods.
The default implementation does nothing.
$invalid_html = $class->mark_form_invalid(html => $html)
This method is used to mark the form fields created by input_form()
invalid when the element fails validate(). The default implementation
wraps the form in <span class="invalid"></span>. This method
may be overridden if the class uses HTML which would be broken by
being wrapped in a span.
$class->load_query_data(element => $element, query => $query)
This call loads the data from the current query into the object.
After this call, $element->data() must return the value
specified by the user in the form fields provided by
display_form().
The default implementation loads data from the query using
$element-data()> for this element's parameter.
$class->check_data(element => $element, data => $data)
This method is called when $element->data() is called to set a
new value for the element. It may be used to validate the data, in
which case it should croak() if the data is invalid. Classes which
require a particular data structure in $element->data() should
override this method. The default implementation does nothing.
$text = $class->freeze_data(element => $element)
Custom serialization of data from the element. This is used to store data in the database and for story serialization.
The default implementation returns $element->data() as-is.
$class->thaw_data(element => $element, data => $text)
Custom deserialization of data, yeilding an element of this class. This is used to load data from the database and in loading serialized stories.
The default implementation just calls $element->data($text).
$index_data = $class->index_data(element => $element)
@index_data = $class->index_data(element => $element)
This method is used when the indexed attribute is true. Elements
which are indexed may be used in calls to Krang::Story->find(). The
return value is limited to 256 characters in length. A list of
strings may be returned which will index against multiple values. The
default implementation returns $element->freeze_data().
$template_data = $class->template_data()
This method returns the data associated with the element, formatted
for use in an output template. In most cases, what
$element->template_data() returns is identical to $element->data(),
but some element classes may override this method to return something
different.
$class->linked_stories(element => $element, publisher => $publisher, story_link => $story_links)
This method allows elementclasses such as text fields or WYSIWYG
editors to hook into the publishers linked-stories-mechanism. The
$story_link hashref passed in is supposed to be filled with key/value
pairs mapping story IDs to story objects. The default implementation
does nothing. For an example implementation see
Krang::ElementClass::PoorText and the latter's template_data()
method.
$filtered = $class->filter_element_data(element => $element, query => $query)
This method is called by Krang::CGI::ElementEditor's dispatcher method filter_element_data(). It should return the filtered (scrubbed/sanitized/cleaned) data for the element it recieves. The default implementation returns the element's data as-is.
$html_tmpl = $class->find_template(element => $element, publisher => $publisher)
Part of the publish/output section of Krang::ElementClass. This call searches the filesystem for the appropriate output template to use with this element.
If successful, it will return an instantiated HTML::Template::Expr object with the selected template loaded.
Parameters are as follows:
publisher - The Krang::Publisher object handling the current publish run.
element - The element currently being published - a Krang::Element object.
filename - The name of the template that should be found. If this parameter is not set, find_template() will search for $element->name . '.tmpl'.
publisher is a required argument, and either element or
filename must be passed in as well.
The default process by which a template is loaded goes as follows:
The name of the template being searched for is $class->name() . ``.tmpl''
Instead of element, filename arg can be passed in, which should correspond
to the template name you are looking for.
The search starts in the directory $publisher->category->url().
If the template is found, it is loaded into an HTML::Template::Expr object. NOTE - Need rules on checking for deployment/preview settings
If the template is not found, move to the parent directory and repeat the search.
If the root directory is reached, no template exists. Croak.
$class->fill_template(element => $element, tmpl => $html_template, publisher => $publisher)
Part of the publish/output section of Krang::ElementClass. This call is responsible for populating the output template of the element with the content stored within. This replaces the ``autofill'' and .pl files that were found in Bricolage.
The default implementation walks the element tree by calling
$child->publish() on all children of the current element. If you
decide to override fill_template, but don't want to deal with the
manual work of walking the element tree, make sure to make a call to
$self->SUPER::fill_template().
The default implementation populates the template as follows:
A single variable is created for $element->name().
For each UNIQUELY NAMED child element used in the template,
a single variable called $childname is created. For
example, if the element contains children named (paragraph,
paragraph, deck), and both <tmpl_var paragraph> and
<tmpl_var deck> are included in the template, two variables
would be created, paragraph and deck. The value of paragraph
would correspond to the first paragraph child element.
If the template has a loop named after a specific child
(e.g. page_loop) it is created as follows:
If the inside of the loop contains a direct reference to the child
- e.g. <tmpl_var page> - and the child is either a primitive element
or a container for which a separate template exists (e.g. page.tmpl),
then each row of the loop will contain $childname = HTML, where HTML
is the result of publishing $child. If not, each row will contain the
vars returned by $child->fill_template() (i.e. any of its OWN children
used in the template - <tmpl_var paragraph> etc. - will be populated).
Either way, each row will also contain the variable $childname_count
(e.g. page_count)
If the template contains an element_loop (i.e. <tmpl_loop element_loop>), it
is created with a row for every child element. The variables are
the same as for the child-specific loop above with the addition of a boolean
is_$childname.
(Note: If the template contains multiple instances of the same loop, each will
be populated with identical variables. This means that if ANY of them contains a
direct reference to the child, they will all have access to $childname = HTML,
and none will have access to the child's own children.)
A loop contrib_loop that contains all contributor information. See
the section on Contributors
If the element is pageable (see Krang::Element), a series of variables relating to Pagination. See the section on Pagination.
A variable for the total number of child elements named with the element name and a trailing _total.
A variable called title containing $story->title().
A variable called slug containing $story->slug().
A variable called page_break containing Krang::Publisher->page_break()
NOTE: If you're developing your own elements, be aware that there exists the potential for naming collisions. For example, if you create a child element named title, you create a collsion with $story->title. Default behavior is that element children take precedence over everything else in a naming collision. Avoid this by choosing better names for your elements.
$html = $class->publish(element => $element, publisher => $publisher)
The API frontend of the publish/output section of Krang::ElementClass. This sub builds the HTML represented by this object and the element tree beneath it. The default implementation ties find_template() and fill_template() together.
If successful, publish() will return a block of HTML.
Generally, you will not want to override publish(). Changes to template-handling behavior should be done by overriding find_template(). Changes to the parameters being passed to the template should be done by overriding fill_template(). Override publish() only in the event that neither of the previous solutions work for you.
NOTE: Some elements are simply attributes with a value, and no
formatting to be associated with them. This can be because the
developer of the element tree wants to handle formatting in the parent
element's template, or that there should be no formatting of the data
whatsoever (e.g. $element->template_data() might get embedded in an
<input> tag).
In these cases, the element will have no template associated with it -
which will cause find_template to fail. If the element has no
children, the value of $element->template_data() will be returned as
the result of the publish() call. If the element *does* have
children, publish() will propagate the error, causing fill_template()
to make the element's children available directly to its parent's template.
$class->freeze_data_xml(element => $element, writer => $writer, set => $set)
This call must serialize the element's data as XML. The default
implementation uses $element->freeze_data() to get a textual
representation and then produces something like this:
<data>$element->freeze_data</data>
The data element is the only valid element that may be written but it may be repeated multiple times. For example, a keywords element might serialize as:
<data>keyword1</data>
<data>keyword2</data>
<data>keyword3</data>
$class->thaw_data_xml(element => $element, data => $data, set => $set)
This call must deserialize the element's data from XML source produced by freeze_data_xml. The data argument points to an array of strings produced by parsing the <data> tags with XML::Simple. For example, if the source XML is:
<data>keyword1</data>
<data>keyword2</data>
<data>keyword3</data>
Then data witll contain:
[ "keyword1", "keyword2", "keyword3" ]
The default implementation calls thaw_data() on $data[0].
$class->order_of_available_children()
This optional method allows an element class to override Krang's default behavior of sorting available children (in the 'Add' dropdown) by display name. If present it can return an array containing the names of optional elements in the desired order, or an array of hashrefs to include optgroups in the menu. The hashrefs should have the following structure:
{
optgroup => 'My Group',
elements => [qw(
element1
element2
element3
)]
}
$class->clone_hook(element => $element)
Called after the story/category containing the element tree is cloned and just before it is saved. The default implementation does nothing.
Element classes should be able to control linked_stories and linked_media
Element deserializer should enforce min/max rules.
malformed element tree in category doesn't throw an error right
Krang::DataSet should check for element set mismatches since Krang::Element is lazy now
deserialize_xml methods are trusting incoming urls, but they don't actually use them...