1 package SL::Presenter::Tag;
5 use parent qw(Exporter);
7 use Exporter qw(import);
8 our @EXPORT = qw(html_tag input_tag man_days_tag name_to_id select_tag stringify_attributes);
12 my %_valueless_attributes = map { $_ => 1 } qw(
13 checked compact declare defer disabled ismap multiple noresize noshade nowrap
18 my ($object, $method, @params) = @_;
19 return $object->$method(@params);
22 { # This will give you an id for identifying html tags and such.
23 # It's guaranteed to be unique unless you exceed 10 mio calls per request.
24 # Do not use these id's to store information across requests.
25 my $_id_sequence = int rand 1e7;
27 return ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
32 sub stringify_attributes {
33 my ($self, %params) = @_;
36 while (my ($name, $value) = each %params) {
38 next if $_valueless_attributes{$name} && !$value;
39 $value = '' if !defined($value);
40 push @result, $_valueless_attributes{$name} ? $self->escape($name) : $self->escape($name) . '="' . $self->escape($value) . '"';
43 return @result ? ' ' . join(' ', @result) : '';
47 my ($self, $tag, $content, %params) = @_;
48 my $attributes = $self->stringify_attributes(%params);
50 return "<${tag}${attributes}>" unless defined($content);
51 return "<${tag}${attributes}>${content}</${tag}>";
55 my ($self, $name, $value, %attributes) = @_;
57 _set_id_attribute(\%attributes, $name);
58 $attributes{type} ||= 'text';
60 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
64 my ($self, $name, $object, %attributes) = @_;
66 my $size = delete($attributes{size}) || 5;
70 my $time_selection = $self->input_tag( "${name}_as_man_days_string", _call_on($object, "${method}_as_man_days_string"), %attributes, size => $size);
71 my $unit_selection = $self->select_tag("${name}_as_man_days_unit", [[ 'h', $::locale->text('h') ], [ 'man_day', $::locale->text('MD') ]],
72 %attributes, default => _call_on($object, "${method}_as_man_days_unit"));
74 return $time_selection . $unit_selection;
78 my ($self, $name) = @_;
80 $name =~ s/\[\+?\]/ _id() /ge; # give constructs with [] or [+] unique ids
81 $name =~ s/[^\w_]/_/g;
88 my ($self, $name, $collection, %attributes) = @_;
90 _set_id_attribute(\%attributes, $name);
92 my $value_key = delete($attributes{value_key}) || 'id';
93 my $title_key = delete($attributes{title_key}) || $value_key;
94 my $default_key = delete($attributes{default_key}) || 'selected';
97 my $value_title_sub = delete($attributes{value_title_sub});
99 my $value_sub = delete($attributes{value_sub});
100 my $title_sub = delete($attributes{title_sub});
101 my $default_sub = delete($attributes{default_sub});
103 my $with_empty = delete($attributes{with_empty});
104 my $empty_title = delete($attributes{empty_title});
106 my $with_optgroups = delete($attributes{with_optgroups});
108 my $default = delete($attributes{default});
110 my $normalize_entry = sub {
111 my ($type, $entry, $sub, $key) = @_;
113 return $sub->($entry) if $sub;
115 my $ref = ref($entry);
118 return $entry if $type eq 'value' || $type eq 'title';
122 if ( $ref eq 'ARRAY' ) {
123 return $entry->[ $type eq 'value' ? 0 : $type eq 'title' ? 1 : 2 ];
126 return $entry->{$key} if $ref eq 'HASH';
127 return $entry->$key if $type ne 'default' || $entry->can($key);
131 my %selected = map { ( $normalize_entry->('value', $_, $default_sub, $default_key) => 1 ) }
132 (@{ ref($default) eq 'ARRAY' ? $default : [ $default ]});
134 my $list_to_code = sub {
135 my ($sub_collection) = @_;
138 foreach my $entry ( @{ $sub_collection } ) {
142 if ( $value_title_sub ) {
143 ($value, $title) = @{ $value_title_sub->($entry) };
146 $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
147 $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
150 my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
152 push(@options, [$value, $title, !!$selected{$value}]);
155 return join '', map { $self->html_tag('option', $self->escape($_->[1]), value => $_->[0], selected => $_->[2]) } @options;
159 $code .= $self->html_tag('option', $self->escape($empty_title || ''), value => '') if $with_empty;
161 if (!$with_optgroups) {
162 $code .= $list_to_code->($collection);
165 $code .= join '', map {
166 my ($optgroup_title, $sub_collection) = @{ $_ };
167 $self->html_tag('optgroup', $list_to_code->($sub_collection), label => $optgroup_title)
171 return $self->html_tag('select', $code, %attributes, name => $name);
174 sub _set_id_attribute {
175 my ($attributes, $name) = @_;
177 $attributes->{id} = name_to_id(undef, $name) if !delete($attributes->{no_id}) && !$attributes->{id};
179 return %{ $attributes };
191 SL::Presenter::Tag - Layouting / tag generation
195 Usage from a template:
199 [% P.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
201 [% P.select_tag('direction', [ { direction => 'left', display => 'To the left' },
202 { direction => 'right', display => 'To the right' } ],
203 value_key => 'direction', title_key => 'display', default => 'right')) %]
205 [% P.select_tag('direction', [ { direction => 'left', display => 'To the left' },
206 { direction => 'right', display => 'To the right', selected => 1 } ],
207 value_key => 'direction', title_key => 'display')) %]
209 # With Rose::DB::Object instances. For example a group membership
210 # (SL::DB::AuthGroup) for a user (SL::DB::AuthUser) via the user's
211 # "groups" relationship:
212 [% P.select_tag('direction', SELF.all_groups, default=SELF.user.groups,
213 title_key='name', default_key='id', multiple=1) %]
217 A module modeled a bit after Rails' ActionView helpers. Several small
218 functions that create HTML tags from various kinds of data sources.
220 The C<id> attribute is usually calculated automatically. This can be
221 overridden by either specifying an C<id> attribute or by setting
226 =head2 LOW-LEVEL FUNCTIONS
230 =item C<html_tag $tag_name, $content_string, %attributes>
232 Creates an opening and closing HTML tag for C<$tag_name> and puts
233 C<$content_string> between the two. If C<$content_string> is undefined
234 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
235 are key/value pairs added to the opening tag.
237 C<$content_string> is not HTML escaped.
239 =item C<name_to_id $name>
241 Converts a name to a HTML id by replacing various characters.
243 =item C<stringify_attributes %items>
245 Creates a string from all elements in C<%items> suitable for usage as
246 HTML tag attributes. Keys and values are HTML escaped even though keys
247 must not contain non-ASCII characters for browsers to accept them.
251 =head2 HIGH-LEVEL FUNCTIONS
255 =item C<input_tag $name, $value, %attributes>
257 Creates a HTML 'input type=text' tag named C<$name> with the value
258 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
259 tag's C<id> defaults to C<name_to_id($name)>.
261 =item C<man_days_tag $name, $object, %attributes>
263 Creates two HTML inputs: a text input for entering a number and a drop
264 down box for chosing the unit (either 'man days' or 'hours').
266 C<$object> must be a L<Rose::DB::Object> instance using the
267 L<SL::DB::Helper::AttrDuration> helper.
269 C<$name> is supposed to be the name of the underlying column,
270 e.g. C<time_estimation> for an instance of
271 C<SL::DB::RequirementSpecItem>. If C<$name> has the form
272 C<prefix.method> then the full C<$name> is used for the input's base
273 names while the methods called on C<$object> are only the suffix. This
274 makes it possible to write statements like e.g.
276 [% P.man_days_tag("requirement_spec_item.time_estimation", SELF.item) %]
278 The attribute C<size> can be used to set the text input's size. It
281 =item C<select_tag $name, \@collection, %attributes>
283 Creates a HTML 'select' tag named C<$name> with the contents of one
284 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
285 HTML attributes from C<%attributes>. The value
286 to use and the title to display are extracted from the elements in
287 C<\@collection>. Each element can be one of four things:
291 =item 1. An array reference with at least two elements. The first element is
292 the value, the second element is its title. The third element is optional and and should contain a boolean.
293 If it is true, than the element will be used as default.
295 =item 2. A scalar. The scalar is both the value and the title.
297 =item 3. A hash reference. In this case C<%attributes> must contain
298 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
299 for the value, title and default respectively.
301 =item 4. A blessed reference. In this case C<%attributes> must contain
302 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
303 reference whose return values are used as the value, title and default
308 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
309 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
310 and C<$attributes{default_key}> defaults to C<selected>.
312 In addition to pure keys/method you can also provide coderefs as I<value_sub>
313 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
314 and are called with the element as first argument. It must return the value, title or default.
316 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
317 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
318 element and must return a list of value and title.
320 If the option C<with_empty> is set then an empty element (value
321 C<undef>) will be used as the first element. The title to display for
322 this element can be set with the option C<empty_title> and defaults to
325 The option C<default> can be either a scalar or an array reference
326 containing the values of the options which should be set to be
327 selected. How the value from the elements is derived depends on three
328 things: the parameters C<default_sub> (unset by default) and
329 C<default_key> (default: "selected") as well as the element's type.
333 =item * If C<default_sub> is a code reference then that reference is
334 called with the element as its only parameter. The return value is the
335 value compared to the value from the elements in C<\@collection>.
337 =item * If the element in question is a scalar then its value is used.
339 =item * If the element is a hash then C<default_key> names the index
340 into the hash used as the value to select by default.
342 =item * If the element is a blessed object then C<default_key> names
343 the method to call on the object. That method's return value is used
344 as the value to select by default.
348 See the synopsis for an example using C<default> with Rose::DB::Object
351 The tag's C<id> defaults to C<name_to_id($name)>.
353 If the option C<with_optgroups> is set then this function expects
354 C<\@collection> to be one level deeper. The upper-most level is
355 translated into a HTML C<optgroup> tag. So the structure becomes:
359 =item 1. Array of array references. Each element in the
360 C<\@collection> is converted into an optgroup.
362 =item 2. The optgroup's C<label> attribute will be set to the the
363 first element in the array element. The second array element is then
364 converted to a list of C<option> tags like it is described above.
368 Example for use of optgroups:
370 # First in a controller:
372 [ t8("First optgroup with two items"),
373 [ { id => 42, name => "item one" },
374 { id => 54, name => "second item" },
375 { id => 23, name => "and the third one" },
377 [ t8("Another optgroup, with a lot of items from Rose"),
378 SL::DB::Manager::Customer->get_all_sorted ],
381 # Later in the template:
382 [% L.select_tag('the_selection', COLLECTION, with_optgroups=1, title_key='name') %]
392 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>,
393 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>