1 package SL::Template::Plugin::L;
3 use base qw( Template::Plugin );
5 use List::MoreUtils qw(apply);
6 use List::Util qw(max);
7 use Scalar::Util qw(blessed);
13 { # This will give you an id for identifying html tags and such.
14 # It's guaranteed to be unique unless you exceed 10 mio calls per request.
15 # Do not use these id's to store information across requests.
16 my $_id_sequence = int rand 1e7;
18 return "id_" . ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
22 my %_valueless_attributes = map { $_ => 1 } qw(
23 checked compact declare defer disabled ismap multiple noresize noshade nowrap
29 return $::locale->quote_special_chars('HTML', $string);
34 $string =~ s/(\"|\'|\\)/\\$1/g;
39 return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
43 my ($class, $context, @args) = @_;
51 die 'not an accessor' if @_ > 1;
52 return $_[0]->{CONTEXT};
59 $name =~ s/[^\w_]/_/g;
66 my ($self, @slurp) = @_;
67 my %options = _hashify(@slurp);
70 while (my ($name, $value) = each %options) {
72 next if $_valueless_attributes{$name} && !$value;
73 $value = '' if !defined($value);
74 push @result, $_valueless_attributes{$name} ? _H($name) : _H($name) . '="' . _H($value) . '"';
77 return @result ? ' ' . join(' ', @result) : '';
81 my ($self, $tag, $content, @slurp) = @_;
82 my $attributes = $self->attributes(@slurp);
84 return "<${tag}${attributes}>" unless defined($content);
85 return "<${tag}${attributes}>${content}</${tag}>";
89 my ($self, @slurp) = @_;
90 my %options = _hashify(@slurp);
94 return $self->html_tag('img', undef, %options);
100 my $collection = shift;
101 my %attributes = _hashify(@_);
103 $attributes{id} ||= $self->name_to_id($name);
105 my $value_key = delete($attributes{value_key}) || 'id';
106 my $title_key = delete($attributes{title_key}) || $value_key;
107 my $default_key = delete($attributes{default_key}) || 'selected';
110 my $value_title_sub = delete($attributes{value_title_sub});
112 my $value_sub = delete($attributes{value_sub});
113 my $title_sub = delete($attributes{title_sub});
114 my $default_sub = delete($attributes{default_sub});
116 my $with_empty = delete($attributes{with_empty});
117 my $empty_title = delete($attributes{empty_title});
121 if ( ref($attributes{default}) eq 'ARRAY' ) {
123 foreach my $entry (@{$attributes{default}}) {
124 $selected{$entry} = 1;
126 } elsif ( defined($attributes{default}) ) {
127 $selected{$attributes{default}} = 1;
130 delete($attributes{default});
136 push(@options, [undef, $empty_title || '']);
139 my $normalize_entry = sub {
141 my ($type, $entry, $sub, $key) = @_;
144 return $sub->($entry);
147 my $ref = ref($entry);
151 if ( $type eq 'value' || $type eq 'title' ) {
158 if ( $ref eq 'ARRAY' ) {
160 if ( $type eq 'value' ) {
164 if ( $type eq 'title' ) {
171 if ( $ref eq 'HASH' ) {
172 return $entry->{$key};
175 if ( $type ne 'default' || $entry->can($key) ) {
182 foreach my $entry ( @{ $collection } ) {
186 if ( $value_title_sub ) {
187 ($value, $title) = @{ $value_title_sub->($entry) };
190 $value = $normalize_entry->('value', $entry, $value_sub, $value_key);
191 $title = $normalize_entry->('title', $entry, $title_sub, $title_key);
194 my $default = $normalize_entry->('default', $entry, $default_sub, $default_key);
196 push(@options, [$value, $title, $default]);
199 foreach my $entry (@options) {
200 if ( exists($selected{$entry->[0]}) ) {
207 foreach my $entry (@options) {
208 my %args = (value => $entry->[0]);
210 $args{selected} = $entry->[2];
212 $code .= $self->html_tag('option', _H($entry->[1]), %args);
215 $code = $self->html_tag('select', $code, %attributes, name => $name);
221 my ($self, $name, $content, @slurp) = @_;
222 my %attributes = _hashify(@slurp);
224 $attributes{id} ||= $self->name_to_id($name);
225 $attributes{rows} *= 1; # required by standard
226 $attributes{cols} *= 1; # required by standard
227 $content = $content ? _H($content) : '';
229 return $self->html_tag('textarea', $content, %attributes, name => $name);
233 my ($self, $name, @slurp) = @_;
234 my %attributes = _hashify(@slurp);
236 $attributes{id} ||= $self->name_to_id($name);
237 $attributes{value} = 1 unless defined $attributes{value};
238 my $label = delete $attributes{label};
239 my $checkall = delete $attributes{checkall};
241 if ($attributes{checked}) {
242 $attributes{checked} = 'checked';
244 delete $attributes{checked};
247 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'checkbox');
248 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
249 $code .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
254 sub radio_button_tag {
257 my %attributes = _hashify(@_);
259 $attributes{value} = 1 unless defined $attributes{value};
260 $attributes{id} ||= $self->name_to_id($name . "_" . $attributes{value});
261 my $label = delete $attributes{label};
263 if ($attributes{checked}) {
264 $attributes{checked} = 'checked';
266 delete $attributes{checked};
269 my $code = $self->html_tag('input', undef, %attributes, name => $name, type => 'radio');
270 $code .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
276 my ($self, $name, $value, @slurp) = @_;
277 my %attributes = _hashify(@slurp);
279 $attributes{id} ||= $self->name_to_id($name);
280 $attributes{type} ||= 'text';
282 return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
286 return shift->input_tag(@_, type => 'hidden');
290 my ($self, $content, @slurp) = @_;
291 return $self->html_tag('div', $content, @slurp);
295 my ($self, $content, @slurp) = @_;
296 return $self->html_tag('ul', $content, @slurp);
300 my ($self, $content, @slurp) = @_;
301 return $self->html_tag('li', $content, @slurp);
305 my ($self, $href, $content, @slurp) = @_;
306 my %params = _hashify(@slurp);
310 return $self->html_tag('a', $content, %params, href => $href);
314 my ($self, $name, $value, @slurp) = @_;
315 my %attributes = _hashify(@slurp);
317 if ( $attributes{confirm} ) {
318 $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
321 return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
325 my ($self, $onclick, $value, @slurp) = @_;
326 my %attributes = _hashify(@slurp);
328 $attributes{id} ||= $self->name_to_id($attributes{name}) if $attributes{name};
329 $attributes{type} ||= 'button';
331 return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
335 my ($self, $name, $value) = splice @_, 0, 3;
336 my %attributes = _hashify(@_);
338 return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
342 my ($self, $data) = @_;
343 return $self->html_tag('script', $data, type => 'text/javascript');
350 foreach my $file (@_) {
351 $file .= '.css' unless $file =~ m/\.css$/;
352 $file = "css/${file}" unless $file =~ m|/|;
354 $code .= qq|<link rel="stylesheet" href="${file}" type="text/css" media="screen" />|;
361 my ($self, $name, $value, @slurp) = @_;
362 my %params = _hashify(@slurp);
363 my $name_e = _H($name);
365 my $datefmt = apply {
369 } $::myconfig{"dateformat"};
371 my $cal_align = delete $params{cal_align} || 'BR';
372 my $onchange = delete $params{onchange};
373 my $str_value = blessed $value ? $value->to_lxoffice : $value;
375 $self->input_tag($name, $str_value,
378 title => _H($::myconfig{dateformat}),
379 onBlur => 'check_right_date_format(this)',
381 onChange => $onchange,
384 ) . ((!$params{no_cal} && !$params{readonly}) ?
385 $self->html_tag('img', undef,
386 src => 'image/calendar.png',
387 alt => $::locale->text('Calendar'),
389 title => _H($::myconfig{dateformat}),
393 "Calendar.setup({ inputField: '$name_e', ifFormat: '$datefmt', align: '$cal_align', button: 'trigger$seq' });"
397 sub customer_picker {
398 my ($self, $name, $value, %params) = @_;
399 my $name_e = _H($name);
401 $::request->{layout}->add_javascripts('autocomplete_customer.js');
403 $self->hidden_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'customer_autocomplete') .
404 $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params);
407 # simple version with select_tag
408 sub vendor_selector {
409 my ($self, $name, $value, %params) = @_;
411 my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
412 (ref $value && $value->can('id')) ? $value->id : '';
414 return $self->select_tag($name, SL::DB::Manager::Vendor->get_all(),
415 default => $actual_vendor_id,
416 title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
422 # simple version with select_tag
424 my ($self, $name, $value, %params) = @_;
426 my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
427 (ref $value && $value->can('id')) ? $value->id : '';
429 return $self->select_tag($name, SL::DB::Manager::Part->get_all(),
430 default => $actual_part_id,
431 title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
441 foreach my $file (@_) {
442 $file .= '.js' unless $file =~ m/\.js$/;
443 $file = "js/${file}" unless $file =~ m|/|;
445 $code .= qq|<script type="text/javascript" src="${file}"></script>|;
452 my ($self, $tabs, @slurp) = @_;
453 my %params = _hashify(@slurp);
454 my $id = $params{id} || 'tab_' . _tag_id();
456 $params{selected} *= 1;
458 die 'L.tabbed needs an arrayred of tabs for first argument'
459 unless ref $tabs eq 'ARRAY';
461 my (@header, @blocks);
462 for my $i (0..$#$tabs) {
463 my $tab = $tabs->[$i];
467 my $selected = $params{selected} == $i;
468 my $tab_id = "__tab_id_$i";
469 push @header, $self->li_tag(
470 $self->link('', $tab->{name}, rel => $tab_id),
471 ($selected ? (class => 'selected') : ())
473 push @blocks, $self->div_tag($tab->{data},
474 id => $tab_id, class => 'tabcontent');
477 return '' unless @header;
478 return $self->ul_tag(
479 join('', @header), id => $id, class => 'shadetabs'
482 join('', @blocks), class => 'tabcontentstyle'
485 qq|var $id = new ddtabcontent("$id");$id.setpersist(true);| .
486 qq|$id.setselectedClassTarget("link");$id.init();|
491 my ($self, $name, $src, @slurp) = @_;
492 my %params = _hashify(@slurp);
494 $params{method} ||= 'process';
496 return () if defined $params{if} && !$params{if};
499 if ($params{method} eq 'raw') {
501 } elsif ($params{method} eq 'process') {
502 $data = $self->_context->process($src, %{ $params{args} || {} });
504 die "unknown tag method '$params{method}'";
507 return () unless $data;
509 return +{ name => $name, data => $data };
513 my ($self, $name, $value, @slurp) = @_;
514 my %attributes = _hashify(@slurp);
517 my $min = delete $attributes{min_rows} || 1;
519 if (exists $attributes{cols}) {
520 $cols = delete $attributes{cols};
521 $rows = $::form->numtextrows($value, $cols);
523 $rows = delete $attributes{rows} || 1;
527 ? $self->textarea_tag($name, $value, %attributes, rows => max($rows, $min), ($cols ? (cols => $cols) : ()))
528 : $self->input_tag($name, $value, %attributes, ($cols ? (size => $cols) : ()));
531 sub multiselect2side {
532 my ($self, $id, @slurp) = @_;
533 my %params = _hashify(@slurp);
535 $params{labelsx} = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
536 $params{labeldx} = "\"" . _J($params{labeldx} || $::locale->text('Selected')) . "\"";
537 $params{moveOptions} = 'false';
539 my $vars = join(', ', map { "${_}: " . $params{$_} } keys %params);
541 <script type="text/javascript">
542 \$().ready(function() {
543 \$('#${id}').multiselect2side({ ${vars} });
551 sub sortable_element {
552 my ($self, $selector, @slurp) = @_;
553 my %params = _hashify(@slurp);
555 my %attributes = ( distance => 5,
556 helper => <<'JAVASCRIPT' );
557 function(event, ui) {
558 ui.children().each(function() {
559 $(this).width($(this).width());
567 if ($params{url} && $params{with}) {
568 my $as = $params{as} || $params{with};
569 my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
570 $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
572 $stop_event = <<JAVASCRIPT;
573 \$.post('$params{url}', { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
577 if (!$params{dont_recolor}) {
578 $stop_event .= <<JAVASCRIPT;
579 \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
580 \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
585 $attributes{stop} = <<JAVASCRIPT;
586 function(event, ui) {
593 $params{handle} = '.dragdrop' unless exists $params{handle};
594 $attributes{handle} = "'$params{handle}'" if $params{handle};
596 my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
598 my $code = <<JAVASCRIPT;
599 <script type="text/javascript">
601 \$( "${selector}" ).sortable({ ${attr_str} })
609 sub online_help_tag {
610 my ($self, $tag, @slurp) = @_;
611 my %params = _hashify(@slurp);
612 my $cc = $::myconfig{countrycode};
613 my $file = "doc/online/$cc/$tag.html";
614 my $text = $params{text} || $::locale->text('Help');
616 die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
617 return unless -f $file;
618 return $self->html_tag('a', $text, href => $file, class => 'jqModal')
623 require Data::Dumper;
624 return '<pre>' . Data::Dumper::Dumper(@_) . '</pre>';
628 my ($self, $text, @slurp) = @_;
629 my %params = _hashify(@slurp);
632 $params{at} = 3 if 3 > $params{at};
635 return $text if length($text) < $params{at};
636 return substr($text, 0, $params{at}) . '...';
639 sub sortable_table_header {
640 my ($self, $by, @slurp) = @_;
641 my %params = _hashify(@slurp);
643 my $controller = $self->{CONTEXT}->stash->get('SELF');
644 my $sort_spec = $controller->get_sort_spec;
645 my $by_spec = $sort_spec->{$by};
646 my %current_sort_params = $controller->get_current_sort_params;
647 my ($image, $new_dir) = ('', $current_sort_params{dir});
648 my $title = delete($params{title}) || $::locale->text($by_spec->{title});
650 if ($current_sort_params{by} eq $by) {
651 my $current_dir = $current_sort_params{dir} ? 'up' : 'down';
652 $image = '<img border="0" src="image/' . $current_dir . '.png">';
653 $new_dir = 1 - ($current_sort_params{dir} || 0);
656 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $by;
657 $params{ $sort_spec->{FORM_PARAMS}->[1] } = ($new_dir ? '1' : '0');
659 return '<a href="' . $controller->get_callback(%params) . '">' . _H($title) . $image . '</a>';
662 sub paginate_controls {
665 my $controller = $self->{CONTEXT}->stash->get('SELF');
666 my $paginate_spec = $controller->get_paginate_spec;
667 my %paginate_params = $controller->get_current_paginate_params;
669 my %template_params = (
670 pages => \%paginate_params,
672 my %url_params = _hashify(@_);
673 $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
674 $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
676 return $controller->get_callback(%url_params);
680 return SL::Presenter->get->render('common/paginate', %template_params);
689 SL::Templates::Plugin::L -- Layouting / tag generation
693 Usage from a template:
697 [% L.select_tag('direction', [ [ 'left', 'To the left' ], [ 'right', 'To the right', 1 ] ]) %]
699 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
700 { direction => 'right', display => 'To the right' } ],
701 value_key => 'direction', title_key => 'display', default => 'right')) %]
703 [% L.select_tag('direction', [ { direction => 'left', display => 'To the left' },
704 { direction => 'right', display => 'To the right', selected => 1 } ],
705 value_key => 'direction', title_key => 'display')) %]
709 A module modeled a bit after Rails' ActionView helpers. Several small
710 functions that create HTML tags from various kinds of data sources.
714 =head2 LOW-LEVEL FUNCTIONS
718 =item C<name_to_id $name>
720 Converts a name to a HTML id by replacing various characters.
722 =item C<attributes %items>
724 Creates a string from all elements in C<%items> suitable for usage as
725 HTML tag attributes. Keys and values are HTML escaped even though keys
726 must not contain non-ASCII characters for browsers to accept them.
728 =item C<html_tag $tag_name, $content_string, %attributes>
730 Creates an opening and closing HTML tag for C<$tag_name> and puts
731 C<$content_string> between the two. If C<$content_string> is undefined
732 or empty then only a E<lt>tag/E<gt> tag will be created. Attributes
733 are key/value pairs added to the opening tag.
735 C<$content_string> is not HTML escaped.
739 =head2 HIGH-LEVEL FUNCTIONS
743 =item C<select_tag $name, \@collection, %attributes>
745 Creates a HTML 'select' tag named C<$name> with the contents of one
746 'E<lt>optionE<gt>' tag for each element in C<\@collection> and with arbitrary
747 HTML attributes from C<%attributes>. The value
748 to use and the title to display are extracted from the elements in
749 C<\@collection>. Each element can be one of four things:
753 =item 1. An array reference with at least two elements. The first element is
754 the value, the second element is its title. The third element is optional and and should contain a boolean.
755 If it is true, than the element will be used as default.
757 =item 2. A scalar. The scalar is both the value and the title.
759 =item 3. A hash reference. In this case C<%attributes> must contain
760 I<value_key>, I<title_key> and may contain I<default_key> keys that name the keys in the element to use
761 for the value, title and default respectively.
763 =item 4. A blessed reference. In this case C<%attributes> must contain
764 I<value_key>, I<title_key> and may contain I<default_key> keys that name functions called on the blessed
765 reference whose return values are used as the value, title and default
770 For cases 3 and 4 C<$attributes{value_key}> defaults to C<id>,
771 C<$attributes{title_key}> defaults to C<$attributes{value_key}>
772 and C<$attributes{default_key}> defaults to C<selected>.
774 In addition to pure keys/method you can also provide coderefs as I<value_sub>
775 and/or I<title_sub> and/or I<default_sub>. If present, these take precedence over keys or methods,
776 and are called with the element as first argument. It must return the value, title or default.
778 Lastly a joint coderef I<value_title_sub> may be provided, which in turn takes
779 precedence over the C<value_sub> and C<title_sub> subs. It will only be called once for each
780 element and must return a list of value and title.
782 If the option C<with_empty> is set then an empty element (value
783 C<undef>) will be used as the first element. The title to display for
784 this element can be set with the option C<empty_title> and defaults to
787 The option C<default> can be either a scalar or an array reference
788 containing the values of the options which should be set to be
791 The tag's C<id> defaults to C<name_to_id($name)>.
793 =item C<yes_no_tag $name, $value, %attributes>
795 Creates a HTML 'select' tag with the two entries C<yes> and C<no> by
796 calling L<select_tag>. C<$value> determines
797 which entry is selected. The C<%attributes> are passed through to
800 =item C<input_tag $name, $value, %attributes>
802 Creates a HTML 'input type=text' tag named C<$name> with the value
803 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
804 tag's C<id> defaults to C<name_to_id($name)>.
806 =item C<hidden_tag $name, $value, %attributes>
808 Creates a HTML 'input type=hidden' tag named C<$name> with the value
809 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
810 tag's C<id> defaults to C<name_to_id($name)>.
812 =item C<submit_tag $name, $value, %attributes>
814 Creates a HTML 'input type=submit class=submit' tag named C<$name> with the
815 value C<$value> and with arbitrary HTML attributes from C<%attributes>. The
816 tag's C<id> defaults to C<name_to_id($name)>.
818 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
819 be added via the C<onclick> handler asking the question given with
820 C<$attributes{confirm}>. If request is only submitted if the user
821 clicks the dialog's ok/yes button.
823 =item C<textarea_tag $name, $value, %attributes>
825 Creates a HTML 'textarea' tag named C<$name> with the content
826 C<$value> and with arbitrary HTML attributes from C<%attributes>. The
827 tag's C<id> defaults to C<name_to_id($name)>.
829 =item C<checkbox_tag $name, %attributes>
831 Creates a HTML 'input type=checkbox' tag named C<$name> with arbitrary
832 HTML attributes from C<%attributes>. The tag's C<id> defaults to
833 C<name_to_id($name)>. The tag's C<value> defaults to C<1>.
835 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
836 created with said C<label>. No attribute named C<label> is created in
839 If C<%attributes> contains a key C<checkall> then the value is taken as a
840 JQuery selector and clicking this checkbox will also toggle all checkboxes
841 matching the selector.
843 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
845 Creates a date input field, with an attached javascript that will open a
846 calendar on click. The javascript ist by default anchoered at the bottom right
847 sight. This can be overridden with C<cal_align>, see Calendar documentation for
848 the details, usually you'll want a two letter abbreviation of the alignment.
849 Right + Bottom becomes C<BL>.
851 =item C<radio_button_tag $name, %attributes>
853 Creates a HTML 'input type=radio' tag named C<$name> with arbitrary
854 HTML attributes from C<%attributes>. The tag's C<value> defaults to
855 C<1>. The tag's C<id> defaults to C<name_to_id($name . "_" . $value)>.
857 If C<%attributes> contains a key C<label> then a HTML 'label' tag is
858 created with said C<label>. No attribute named C<label> is created in
861 =item C<javascript_tag $file1, $file2, $file3...>
863 Creates a HTML 'E<lt>script type="text/javascript" src="..."E<gt>'
864 tag for each file name parameter passed. Each file name will be
865 postfixed with '.js' if it isn't already and prefixed with 'js/' if it
866 doesn't contain a slash.
868 =item C<stylesheet_tag $file1, $file2, $file3...>
870 Creates a HTML 'E<lt>link rel="text/stylesheet" href="..."E<gt>' tag
871 for each file name parameter passed. Each file name will be postfixed
872 with '.css' if it isn't already and prefixed with 'css/' if it doesn't
875 =item C<date_tag $name, $value, cal_align =E<gt> $align_code, %attributes>
877 Creates a date input field, with an attached javascript that will open a
878 calendar on click. The javascript ist by default anchoered at the bottom right
879 sight. This can be overridden with C<cal_align>, see Calendar documentation for
880 the details, usually you'll want a two letter abbreviation of the alignment.
881 Right + Bottom becomes C<BL>.
883 =item C<tabbed \@tab, %attributes>
885 Will create a tabbed area. The tabs should be created with the helper function
889 L.tab(LxERP.t8('Basic Data'), 'part/_main_tab.html'),
890 L.tab(LxERP.t8('Custom Variables'), 'part/_cvar_tab.html', if => SELF.display_cvar_tab),
893 An optional attribute is C<selected>, which accepts the ordinal of a tab which
894 should be selected by default.
896 =item C<areainput_tag $name, $content, %PARAMS>
898 Creates a generic input tag or textarea tag, depending on content size. The
899 amount of desired rows must be either given with the C<rows> parameter or can
900 be computed from the value and the C<cols> paramter, Accepted parameters
901 include C<min_rows> for rendering a minimum of rows if a textarea is displayed.
903 You can force input by setting rows to 1, and you can force textarea by setting
906 =item C<multiselect2side $id, %params>
908 Creates a JavaScript snippet calling the jQuery function
909 C<multiselect2side> on the select control with the ID C<$id>. The
910 select itself is not created. C<%params> can contain the following
917 The label of the list of available options. Defaults to the
918 translation of 'Available'.
922 The label of the list of selected options. Defaults to the
923 translation of 'Selected'.
927 =item C<sortable_element $selector, %params>
929 Makes the children of the DOM element C<$selector> (a jQuery selector)
930 sortable with the I<jQuery UI Selectable> library. The children can be
931 dragged & dropped around. After dropping an element an URL can be
932 postet to with the element IDs of the sorted children.
934 If this is used then the JavaScript file C<js/jquery-ui.js> must be
935 included manually as well as it isn't loaded via C<$::form-gt;header>.
937 C<%params> can contain the following entries:
943 The URL to POST an AJAX request to after a dragged element has been
944 dropped. The AJAX request's return value is ignored. If given then
945 C<$params{with}> must be given as well.
949 A string that is interpreted as the prefix of the children's ID. Upon
950 POSTing the result each child whose ID starts with C<$params{with}> is
951 considered. The prefix and the following "_" is removed from the
952 ID. The remaining parts of the IDs of those children are posted as a
953 single array parameter. The array parameter's name is either
954 C<$params{as}> or, missing that, C<$params{with}>.
958 Sets the POST parameter name for AJAX request after dropping an
959 element (see C<$params{with}>).
963 An optional jQuery selector specifying which part of the child element
964 is dragable. If the parameter is not given then it defaults to
965 C<.dragdrop> matching DOM elements with the class C<dragdrop>. If the
966 parameter is set and empty then the whole child element is dragable,
967 and clicks through to underlying elements like inputs or links might
970 =item C<dont_recolor>
972 If trueish then the children will not be recolored. The default is to
973 recolor the children by setting the class C<listrow0> on odd and
974 C<listrow1> on even entries.
980 <script type="text/javascript" src="js/jquery-ui.js"></script>
982 <table id="thing_list">
984 <tr><td>This</td><td>That</td></tr>
987 <tr id="thingy_2"><td>stuff</td><td>more stuff</td></tr>
988 <tr id="thingy_15"><td>stuff</td><td>more stuff</td></tr>
989 <tr id="thingy_6"><td>stuff</td><td>more stuff</td></tr>
993 [% L.sortable_element('#thing_list tbody',
994 url => 'controller.pl?action=SystemThings/reorder',
997 recolor_rows => 1) %]
999 After dropping e.g. the third element at the top of the list a POST
1000 request would be made to the C<reorder> action of the C<SystemThings>
1001 controller with a single parameter called C<thing_ids> -- an array
1002 containing the values C<[ 6, 2, 15 ]>.
1006 Dumps the Argument using L<Data::Dumper> into a E<lt>preE<gt> block.
1008 =item C<sortable_table_header $by, %params>
1010 Create a link and image suitable for placement in a table
1011 header. C<$by> must be an index set up by the controller with
1012 L<SL::Controller::Helper::make_sorted>.
1014 The optional parameter C<$params{title}> can override the column title
1015 displayed to the user. Otherwise the column title from the
1016 controller's sort spec is used.
1018 The other parameters in C<%params> are passed unmodified to the
1019 underlying call to L<SL::Controller::Base::url_for>.
1021 See the documentation of L<SL::Controller::Helper::Sorted> for an
1022 overview and further usage instructions.
1024 =item C<paginate_controls>
1026 Create a set of links used to paginate a list view.
1028 See the documentation of L<SL::Controller::Helper::Paginated> for an
1029 overview and further usage instructions.
1033 =head2 CONVERSION FUNCTIONS
1037 =item C<tab, description, target, %PARAMS>
1039 Creates a tab for C<tabbed>. The description will be used as displayed name.
1040 The target should be a block or template that can be processed. C<tab> supports
1041 a C<method> parameter, which can override the process method to apply target.
1042 C<method => 'raw'> will just include the given text as is. I was too lazy to
1043 implement C<include> properly.
1045 Also an C<if> attribute is supported, so that tabs can be suppressed based on
1046 some occasion. In this case the supplied block won't even get processed, and
1047 the resulting tab will get ignored by C<tabbed>:
1049 L.tab('Awesome tab wih much info', '_much_info.html', if => SELF.wants_all)
1051 =item C<truncate $text, %params>
1053 Returns the C<$text> truncated after a certain number of
1056 The number of characters to truncate at is determined by the parameter
1057 C<at> which defaults to 50. If the text is longer than C<$params{at}>
1058 then it will be truncated and postfixed with '...'. Otherwise it will
1059 be returned unmodified.
1063 =head1 MODULE AUTHORS
1065 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
1067 L<http://linet-services.de>