From: Chris W. <la...@us...> - 2005-09-24 14:01:00
|
Update of /cvsroot/openinteract/OpenInteract2/pkg/base_box/OpenInteract2/App In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17249/OpenInteract2/App Modified Files: BaseBox.pm Log Message: add more docs for building boxes, including a more complex example where we grab data from the database Index: BaseBox.pm =================================================================== RCS file: /cvsroot/openinteract/OpenInteract2/pkg/base_box/OpenInteract2/App/BaseBox.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** BaseBox.pm 29 Mar 2005 02:36:22 -0000 1.3 --- BaseBox.pm 24 Sep 2005 14:00:51 -0000 1.4 *************** *** 84,87 **** --- 84,88 ---- title = Frequent Links security = no + url_none = yes # Reference our template-only box, overriding weight and title: *************** *** 112,124 **** =head1 DESCRIPTION ! Boxes are standalone parcels of content that conform to a particular ! format. The content of a box is simply the result of executing an ! OpenInteract2 action: that action may be a piece of code (method in a ! class) or it may just be a template. In either case the box handler (L<OpenInteract2::Action::Box>) sorts ! the boxes and places the content for each in a 'shell' so all the ! boxes look the same -- if you want them to. The standard box looks ! something like this: ---------------------- <-- 'shell' --- 113,125 ---- =head1 DESCRIPTION ! A box contains content generated by an L<OpenInteract2::Action>. That ! content, plus some extra parameters, fits into a particular form so ! that all boxes look the same (if you want them to). The action doing ! the generating can be a typical class or just a template. In either case the box handler (L<OpenInteract2::Action::Box>) sorts ! the boxes and places the content for each in a 'shell'. The standard ! box looks something like this, as defined by 'class' attributes and ! CSS definitions: ---------------------- <-- 'shell' *************** *** 130,209 **** ---------------------- ! But you can create your own shell for all boxes by setting the 'boxes' ! parameter 'default_box_template' or on a per-box basis with the ! parameter 'box_template'. Whichever you choose, the value should be a ! particular template (in the 'package::template_name' format). ! ! =head1 BOX COLLECTION ! ! =head2 Configuration ! ! The L<OpenInteract2::Action::Box> action has a number of configuration ! parameters that define how the collection of boxes are organized and ! how they look. As with all L<OpenInteract2::Action> objects you can ! define these parameters in a configuration file (C<conf/action.ini> in ! the C<base_box> package) or in the object itself. ! ! =over 4 ! ! =item * ! ! B<default_box_template> ($) (optional) ! ! This is the template into which every box content gets put unless it ! specifies otherwise. The default template is ! C<base_box::main_box_shell>, which as the name would indicate is also ! installed with this package. ! B<default_box_separator> ($) (optional) ! This is the string used to separate boxes. For instance, if you want ! to put a short horizontal rule between each line, you could set this ! to: ! default_box_separator = <hr width="50%" noshade/> ! Or if you had a custom image you wanted to separate your boxes with: ! default_box_separator = <div align="center"><img src="/images/box_sep.gif" height="2" width="25"/></div> ! This module defines the default separator as '<br />'. ! =item * ! B<default_box_weight> ($; optional) ! Use as the box weight if unspecified. ! =item * ! B<system_box_class> ($) (optional) ! Defines what we should run on every request to display system ! boxes. Typically this is C<OpenInteract2::Action::SystemBoxes>; see ! L<OpenInteract2::Action::SystemBoxes> for what this includes. ! It is okay if you blank this out, you just will not get the 'login', ! 'templates used', 'admin tools' and other boxes on every page. ! We call the C<handler()> method on whatever class is defined here. ! =item * ! B<custom_box_class> ($) (optional) ! If you want to call a custom handler to run every time B<in addition ! to> the system handler named above, list the class here. We call the ! C<handler()> method on whatever class is defined here. ! =back ! =head1 BOX SAMPLES ! An individual box also has a say as to how it will be rendered as well ! as the content it will have. We'll go through a few different use ! cases. ! =head2 Sample: Standard action Like we said above, every box is just an action in a different --- 131,206 ---- ---------------------- ! But you can create your own shell for all boxes so they can look like ! this: ! BOX TITLE | Box content ! | generated by an ! | action goes here ! or whatever HTML/CSS magic you can conjure. You can also change the ! shell for a box at a time, provide localization keys instead of ! harcoded titles, provide parameters so that the content is specific to ! the current user, and much more. ! =head1 BOX SAMPLES ! An individual box also has a say as to how it will be rendered as well ! as the content it will have. We'll go through a few different use ! cases. ! =head2 Sample: Template-only, no parameteters ! You can also just pass a template name for a box: ! CTX->controller->add_box( 'mypkg::my_box_template' ); ! Which simply uses the scalar passed in as the template name and the ! box name, and uses all the defaults. However, you will likely get a ! box with a title 'Generic Box', which is probably not what you want. ! =head3 Sidebar: It's still an action... ! Just in case you thought that template-only boxes were an exception to ! the 'every box is an action' rule: underneath the hood we create an ! action of type 'template_only' and just assign the parameter ! 'template' as the template name you pass in. You can do the exact same ! thing by defining your action like this: ! [my_box] ! action_type = template_only ! template = mypkg::my_box_template ! And then adding the box like this: ! CTX->controller->add_box( 'my_box' ); ! The advantage is that the second uses a layer of abstraction for ! flexibility. So if you later want to perform some non-templating ! actions you can change the definition of the 'my_box' action to ! something like: ! [my_box] ! class = OpenInteract2::Action::MyBox ! task = generate_box ! paramA = foo ! ... ! and not have to change anything else. ! =head2 Sample: Template-only, parameters ! Another example -- this time you have to set your template as the ! parameter 'template' since you've got other box-specific properties to ! assign: ! CTX->controller->add_box({ ! name => 'mypkg::my_box_template', ! weight => 1, ! title => 'My First Box' ! }); ! Again, see L<BOX PROPERTIES> for everything you can assign. ! =head2 Sample: Box from simple action Like we said above, every box is just an action in a different *************** *** 226,232 **** 1; ! And our configuration for the box action points to the class and ! method; here's the most basic configuration to which we'll add values ! to see how they're reflected (from the C<conf/action.ini>): [insult_box] --- 223,258 ---- 1; ! Very simple, and like the rest of OpenInteract2 you don't have to ! worry about details like the source agsinst which the user was ! authenticated. Here we only change the content based on the user ID, ! but it would be easy to change it to lookup a fortune: ! ! sub insult { ! my ( $self ) = @_; ! my $first_name = CTX->request->auth_user->first_name; ! my $fortune = `/usr/bin/fortune insult`; ! return "$first_name: $fortune"; ! } ! ! Or say we wanted to lookup an insult based on the geographic IP ! address of the user: ! ! my %INSULTS = ( ! US => "I hear the people in America are so stupid...", ! Canada => "Canadians are so rude...", ! ); ! ! sub insult { ! my ( $self ) = @_; ! my $gi = Geo::IP->new( GEOIP_STANDARD ); ! my $host = CTX->request->remote_host; ! my $country = $gi->country_code_by_addr( $host ); ! return "$first_name: $INSULTS{ $country }"; ! } ! ! All this is hidden -- it's just an action that generates content. Our ! configuration for the box action points to the class and method; ! here's the most basic configuration to which we'll add values to see ! how they're reflected (from the C<conf/action.ini>): [insult_box] *************** *** 235,243 **** We'd add the box from a template like this -- 'insult_box' is from the ! name of our action: [% OI.box_add( 'insult_box' ) %] ! When invoked by the 'boxes' action this will generate something like: ---------------------- --- 261,269 ---- We'd add the box from a template like this -- 'insult_box' is from the ! name of our action found in the INI section heading ('[insult_box]'): [% OI.box_add( 'insult_box' ) %] ! When invoked by the 'boxes' action this might generate something like: ---------------------- *************** *** 274,286 **** class = OpenInteract2::Action::InsultUser task = insult ! title_key = Your Insult And in our localization files we'd have something like: ! myapp-en.msg: ! Your Insult = Your Insult ! ! myapp-es.msg: ! Your Insult = Su Insulto So if I login with my browser's preferred language set to Spanish --- 300,314 ---- class = OpenInteract2::Action::InsultUser task = insult ! title_key = insult.title And in our localization files we'd have something like: ! myapp-en.msg ! ---------- ! insult.title = Your Insult ! ! myapp-es.msg ! ---------- ! insult.title = Su Insulto So if I login with my browser's preferred language set to Spanish *************** *** 294,329 **** ---------------------- ! See below for all the other properties you can assign. ! =head2 Sample: Template-only, no parameteters ! The simplest case is a call: ! CTX->controller->add_box( 'mypkg::my_box_template' ); ! Which simply uses the scalar passed in as the template name and the ! box name, and uses all the defaults. However, you will likely get a ! box with a title 'Generic Box', which is probably not what you want. ! Just in case you thought that template-only boxes were an exception to ! the 'every box is an action' rule: underneath the hood we create an ! action of type 'template_only' and just assign the parameter ! 'template' as the template name you pass in. ! =head2 Sample: Template-only, parameters ! Another example -- this time you have to set your template as the ! parameter 'template' since you've got other parameters: CTX->controller->add_box({ ! name => 'mypkg::mybox', ! weight => 1, ! title => 'My First Box' }); =head1 BOX PROPERTIES Every box can define the following properties: B<name> ($) --- 322,474 ---- ---------------------- ! See below under L<BOX PROPERTIES> for all the other properties you can ! assign. ! =head2 Sample: Box from more complex action with template ! Now we'll show a more complicated box where we retrieve data from a ! database and send them onto a template. This example is from the ! 'comments' package, so you can see it in action whenever you like. ! First, we'll declare the box in the comment package's ! C<conf/action.ini>: ! [comment_recent] ! class = OpenInteract2::Action::Comments ! task = comment_recent ! is_secure = no ! url_none = yes ! cache_expire = 30m ! template_source = comments::box_comment_recent ! title_key = comments.recent ! default_comment_count = 5 ! Note that we're mixing in box properties with the action parameters ! here. For instance, 'title_key' is an action parameter but the box ! processor will find it and translate it to the 'label' field passed to ! the box shell (see L<BOX PROPERTIES> below). But the 'is_secure' tells ! OI2 whether to check security on the action, and 'url_none' tells it ! to ensure the action cannot be called by a URL. ! And since the box is just an action we can take advantage of caching ! as well: the 'cache_expire' action property tells OI2 how long it can ! cache the generated content. This means the database call you'll see ! below will only happen once every 30 minutes. ! Finally, we associate in configuration the action data with a template ! using the 'template_source' action property, which means we won't have ! reference the template in code. ! ! So now let's look at the method we referenced in the 'class' and ! 'task' action properties: ! ! package OpenInteract2::Action::Comments ! ... ! sub comment_recent { ! my ( $self ) = @_; ! my $recent_num = $self->param( 'comment_count' ) || ! $self->param( 'default_comment_count' ) || ! 5; # last-ditch effort... ! my %params = (); ! my $comments = eval { ! OpenInteract2::Comment->fetch_group({ ! limit => $recent_num, ! order => 'posted_on DESC', ! column_group => 'summary', ! }) ! }; ! if ( $@ ) { ! $params{error} = $self->_msg( ! 'comments.error.cannot_fetch_recent', "$@" ); ! } ! else { ! $params{comments} = $comments; ! } ! return $self->generate_content( \%params ); ! } + First we determine how many comments to display. Note that the first + try is to an action parameter 'comment_count'. You can set this when + you add the box: + + From a template: + [% OI.box_add( 'comment_recent', comment_count = 3 ) %] + + From code: CTX->controller->add_box({ ! name => 'comment_recent', ! comment_count => 3, }); + Next we'll retrieve the most recent comments. Comments are represented + by a SPOPS object (L<OpenInteract2::Comment>) which inherits the SPOPS + method <fetch_group()>. That method retieves objects using arbitrary + criteria. + + If we get an error we assign it to the parameter 'error', otherwise we + stuff the retrieved comments in the parameter 'comments'. We then pass + these parameters to the action C<generate_content()> method, which is + smart enough to associate them with the 'template_source' we defined + in the action configuration. + + Here's the template that gets the comments: + + [% IF error %] + + <p>[% error %]</p> + + [% ELSIF comments.size == 0 -%] + + <p>[% MSG( 'comments.no_comments' ) %]</p> + + [% ELSE -%] + + <ul class="listInBox"> + [% FOREACH comment = comments; + object_url = comment.get_summary.object_url; + post_date = OI.date_format( comment.posted_on, '%d-%b %H:%M' ); -%] + <li><a href="[% object_url %]">[% comment.subject %]</a> + on [% post_date %]</li> + [% END %] + </ul> + + [% END -%] + + In the template we'll display the error if it exists, or display a + message that no comments were found if the 'comments' list is empty, + or display a bulleted list of the posted comments. So it might look + like: + + * <mod_perl rocks> on 13-Jan 12:45 + * <I hate faxes> on 13-Jan 9:09 + * <my monitor's great> on 12-Jan 18:19 + + Once that's generated the box processor will place that content into + the box shell. So assuming we can resolve the 'title_key' parameter + (which was 'comments.recent') using the following message key + definition: + + In comments-messages-en.msg: + + comments.recent = Recent Comments + + The final box could look like: + + ----------------------------------------- + | Recent Comments | + ----------------------------------------- + | * <mod_perl rocks> on 13-Jan 12:45 | + | * <I hate faxes> on 13-Jan 9:09 | + | * <my monitor's great> on 12-Jan 18:19| + ----------------------------------------- + =head1 BOX PROPERTIES Every box can define the following properties: + =over 4 + + =item * + B<name> ($) *************** *** 331,339 **** may also be the name of the template for this box. B<box_name> ($) Solely used to identify the box; if not provided we use the 'name' parameter. This is useful if you've got two boxes referencing the same ! template but with different content. For instance, say you had a box 'weather' that displayed weather --- 476,486 ---- may also be the name of the template for this box. + =item * + B<box_name> ($) Solely used to identify the box; if not provided we use the 'name' parameter. This is useful if you've got two boxes referencing the same ! action (or template) but with different content. For instance, say you had a box 'weather' that displayed weather *************** *** 351,354 **** --- 498,528 ---- [% OI.remove_box( 'pgh weather' ) %] + =item * + + B<weight> ($) + + Number between 1 (top) and 10 (bottom) indicating where you want the + box to be. If you do not specify the weight the C<default_box_weight> + action parameter will be used; if that's not defined the world is an + uncertain place, so who knows what'll happen? + + =item * + + B<box_template> ($) (optional) + + If you specify the keyword '_blank_' then your box content will be + 'naked' and not wrapped by anything else. If you leave this empty you + will use the template specified in the 'default_box_template' action + parameter. + + =back + + The 'title' properties will get resolved to a variable 'label' which + is passed to the shell: + + =over 4 + + =item * + B<title> ($) (optional) *************** *** 361,368 **** --- 535,553 ---- 'title' and if both are present this will be used. + =back + + The 'title_image' properties will get resolved to a variable + 'label_image_src' which is passed to the shell: + + =over 4 + + =item * + B<title_image_src> ($) (optional) Display an image for the title to be used in the 'shell' wrapper. + =item * + B<title_image_src_key> ($) (optional) *************** *** 370,377 **** --- 555,573 ---- 'title_image_src' and if both are present this will be used. + =back + + The 'title_image_alt' properties will get resolved to a variable + 'label_image_alt' which is passed to the shell: + + =over 4 + + =item * + B<title_image_alt> ($) (optional) Text to put in the 'alt' tag if using an image in the title. + =item * + B<title_image_alt_key> ($) (optional) *************** *** 380,400 **** this will be used. ! B<weight> ($) ! ! Number between 1 (top) and 10 (bottom) indicating where you want the ! box to be. If you do not specify the weight the C<default_box_weight> ! action parameter will be used; if that's not defined the world is an ! uncertain place. ! B<box_template> ($) (optional) ! If you specify the keyword '_blank_' then your box content will be ! 'naked' and not wrapped by anything else. If you leave this empty you ! will use the template specified in the 'default_box_template' action ! parameter. =item * ! B<*> or B<\%params> (optional) Any additional parameters, or parameters specified in C<\%params>, --- 576,588 ---- this will be used. ! =back ! Finally, everything else: ! =over 4 =item * ! B<*> or B<params> (\%) (optional) Any additional parameters, or parameters specified in C<\%params>, *************** *** 403,406 **** --- 591,664 ---- =back + =head1 BOX COLLECTION + + =head2 Configuration + + The L<OpenInteract2::Action::Box> action has a number of configuration + parameters that define how the collection of boxes are organized and + how they look. As with all L<OpenInteract2::Action> objects you can + define these parameters in a configuration file (C<conf/action.ini> in + the C<base_box> package) or, more rarely, in the object itself. + + =over 4 + + =item * + + B<default_box_template> ($) (optional) + + This is the template into which the content generated by box gets put + unless it specifies otherwise (see 'box_template' in L<BOX + PROPERTIES>, below). The default template is + C<base_box::main_box_shell>, which as the name would indicate is also + installed with this package. + + B<default_box_separator> ($) (optional) + + This is the string used to separate boxes. For instance, if you want + to put a short horizontal rule between each line, you could set this + to: + + default_box_separator = <hr width="50%" noshade /> + + Or if you had a custom image you wanted to separate your boxes with: + + default_box_separator = <div align="center"><img src="/images/box_sep.gif" height="2" width="25"/></div> + + This module defines the default separator as '<br />'. + + =item * + + B<default_box_weight> ($; optional) + + Use as the box weight if unspecified. + + =item * + + B<system_box_class> ($) (optional) + + Defines what we should run on every request to display system + boxes. Typically this is C<OpenInteract2::Action::SystemBoxes>; see + L<OpenInteract2::Action::SystemBoxes> for what this includes. + + It is okay if you blank this out, you just will not get the 'login' + and admin-only 'templates used' 'admin tools' boxes on every page. + + We call the C<handler()> method on whatever class is defined + here. This class does not (and should not) subclass + L<OpenInteract2::Action>, typically it just adds some boxes for later + processing. + + =item * + + B<custom_box_class> ($) (optional) + + If you want to call a custom handler to run every time B<in addition + to> the system handler named above, list the class here. We call the + C<handler()> method on whatever class is defined here. This class does + not (and should not) subclass L<OpenInteract2::Action>, typically it + just adds some boxes for later processing. + + =back + =head1 ACTIONS |