Input.pm
NAME
Communiware::DE::Input - Dynamic element Input
SYNOPSIS
<form> <:Input type name default ...:> ... </form>
DESCRIPTION
Implements form input elements on Communiware pages.
Dynamic element Input differs from the HTML INPUT tag, used in Communiware template, following ways:
-
Default value is placed into Communiware request context.
When form is submitted, user-supplied value is shown instead of default
one.
Template designer can generate popup-menus, scrolling lists and
lists of radio buttons/checkboxes using Communiware filters.
One dynamic element Input can produce a lot of radiobuttons or
checkboxes.
User supplied values can be stored as in the user cookies
Date values can be reformatted upon submit.
If input appears within multi-item posting, names are mangled to make
attributes of different names distinct
Parameters of Input element:
- type
- Defines type of input element. Any value allowed for type attribute of the HTML tag INPUT except image.file and password is allowed. Additionaly, popupmenu, radiogroup and textarea can be used. Input type image can be directly coded into HTML.
- name
- Name of context attribute which would be changed by this dynamic element.
- default
- Value, which this input element has before yser changes it. For submit and reset this arg contain text, which would be displayed on the button.
- type-specific parameters
- Rest of parameters, with possible exception of last one, are type-specific. See description of input types for details
- additional tag attributes
- Last paremeter of Input dynamic element is comma-separated list of name=value pairs, which would be comverted into additional attributes of HTML tag. Typically useful for setting class of element.
INPUT ELEMENT TYPES
- text
- Additiional argument - size of entry field
- date
- Special form of text field - allows to enter date in desired format. First additional argument is date format which is strftime format string with only %d %m %y %Y %H %M and %S specificators allowed.
-
This additional parameter is required.
-
Second additional parmeter is field size.
- textarea
- Additional arguments number of lines and number of columns
- checkbox
- Individual checkbox. Generates either empty attribute or attribute with given default value. Rest of arguments, except last one if it is list of name=value pairs, is interpreted as logical expression, which indicates whether checkbox should be checked.
-
Name=value pairs in the last argument are added to the generated <input> tag as additional attributes. Attribute disabled is treated specially - when in HTML disabled would disable checkbox regardless of its value, in Communiware
disabled=0
or empty string value means that checkbox should be enabled. - radio
- Individual radiobutton. Almost useless unless you generate them by list. Arguments are exactly same as in standalone checkbox
- radiogroup
- Group of radiobuttons. Allows to choose one of them. There are two ways to specify list of values and there labels: using filter to select them from database or using inline list, specified directly in the template. For filter-based approach you need following additional parameters:
- popupmenu
- Roughly same as radiogroup but uses less space of the page.
- scrollinglist
- Roughly same as popupmenu, but allows multiple choice.
- checkboxgroup
- Same as radiogroup, but allows multiple choice
- submit
- reset
- Reset button.
- hidden
- Produces hidden field. If appears in the posting context, prepends correct prefix to the field name
-
Specification of the filter
Name of the field, returned by the filter, which contain value
Name of the field, containing label
Label for the default value. Used if default value is not among those,
returned by filter
For inline approach:
-
word inline specified literally
pairs of value name, as many as you need, in the order they would
appear in the user browser. Each name and each value should be separate
parameter. You should specify default value and its label along others.
Among other tag attributes attribute columns can be used for radiogroup. It specifies how many columns of radiobuttons would be produced.
Submit button. Optionally causes some of user-changable attributes be stored in cookies.
Additional parameters:
-
List of comma-separated attribute=value pairs which would be translated
into additional attributes of input tag. Attribute confirm=message
would produce javascript popup window, which asks user if he really
want to submit this form. NOTE! This option produces onClick action and
therefore it is incompatible with any other explicit onClick action.
list of comma separated attribute names. If supplied, these attributes
would be stored as user cookies. In posting context, only attributes
which exists in posting context may be listed.
Flag indicating domain of cookies. Possible values - server - current
Communiware virtual server, item - current item and exact -
exactly same URL as used in current request (without QUERY_STRING, of
course), site - entire site (all virtual server as well as script
directory. Use this only for creating your own customization forms,
intended to replace default userprefs script.
exec
Communiware::DE::Input::exec($ctx,$type,@params)
Handles Input dynamic elements whose types are not known run_time
=cut
sub exec { my $ctx = shift; my $type = shift; no strict 'refs'; return $types{$type}->($ctx, @_) if exists $types{$type}; return ``<span class=formError>No such Input type $type</span>''; }
sub compile {
my $frag = shift;
my ($type, undef, $is_const) = $frag->param(shift);
my $name = $frag->param(shift);
my $options = $frag->param_list(pop @_) if $_[-1] && $_[-1] =~ /^\w+=/;
push @_, '' if !@_;
$options ||= '{}';
if (!$is_const) {
return $frag->make_exec($type, $name, map(scalar $frag->param($_), @_), $options);
}
else {
$type = eval($type);
$frag->compile_error(``Unknown input type '%s''', $type) unless exists $types{$type};
my $proc = $types{$type};
$proc = ``Communiware::DE::Input::$proc'' unless $proc =~ /::/;
if ($type eq 'checkbox' || $type eq 'radio') {
my $value = $frag->param(shift);
my @expr = @_;
@expr = ('0') unless @expr;
my $expr_code = $frag->compile_expr(\@expr);
$frag->compile_error(``Input %s: Extra args after expression'', $type) if @expr;
return ``\$out .= $proc(\$ctx,$name,$value,$expr_code,$options);\n'';
}
else {
if ($type eq 'submit') {
my $value = shift;
$options = $frag->param_list(shift)
if @_;
unshift @_, $value;
}
if ( grep {/^(popupmenu|checkboxgroup|radiogroup|scrollinglist)$/} $type ) {
my $filter = $_[1];
$frag->uses_filter($filter)
if $filter ne 'inline';
}
return ``\$out .= $proc(\$ctx,$name,''
. join(',', map(scalar($frag->param($_)), @_), $options) . ``);\n'';
}
}
}
sub hiddenfield { return hidden_field($_[0]->prefix . $_[1], $_[2]); }
sub text_input { | |
my $ctx = shift; | |
my $name = shift; | |
my $default = shift; | |
my $opts = pop || {}; | |
my $size = shift; | |
my %args = (%$opts, name => (defined $ctx->prefix ? $ctx->prefix . $name : $name), value => $default); |
if (defined $size && length $size && $size !~ /^\d+$/) { die(sprintf("Parameter size for Input text must be number not '%s'", $size)); } $args{'size'} = $size; delete $args{size} if !$args{size}; my $fctx = $ctx->special_context('form'); if ($fctx && defined $fctx->get($name)) { $args{value} = $fctx->get($name); } delete $args{value} unless defined $args{value} && length $args{value}; $args{'type'} = 'text'; return tag('input', \%args); }
sub date_input { | |
my $ctx = shift; | |
my $name = shift; | |
my $inp_name = (defined $ctx->prefix ? $ctx->prefix . $name : $name); | |
my $default = shift; | |
my $opts = pop || {}; | |
my $format = shift; | |
my $size = shift; |
$opts->{'name'} = "$inp_name.FMT"; $opts->{'type'} = 'text'; $opts->{'size'} = $size if $size;
# replace for default value - get value from global context or if it # doesn't exsists get get current value if (defined(my $fctx = $ctx->special_context('form'))) {
# value from global context must'n be formated if (!defined($opts->{'value'} = $ctx->special_context('global')->get($opts->{'name'}))) {
# get current value by standart way $default = $fctx->get($name) if defined $fctx->get($name); } }
$opts->{'value'} = Communiware::Date::format_date($default, $format) unless defined $opts->{'value'};
return hidden_field("$inp_name.PKT", $format) . hidden_field("dates", $name) . tag('input', $opts); }
sub textarea_input {
my $ctx = shift;
my $name = shift;
my $default = shift;
my $opts = pop;
my $fctx = $ctx->special_context('form');
$default = $fctx->get($name)
if $fctx && defined $fctx->get($name);
my %args = (name => $ctx->prefix . $name, wrap => ``soft'', %$opts);
my $rows = shift;
$args{rows} = $rows if $rows;
my $cols = shift;
$args{cols} = $cols if $cols;
return tag('textarea', \%args, escapeHTML($default));
}
sub checkbox_input { | |
my $ctx = shift; | |
my $name = shift; | |
my $value = shift; | |
my $opts = pop; | |
my %args = (type => 'checkbox', name => $ctx->prefix . $name, value => $value, %$opts); |
# cheking disabled ($args{onclick} = "return false;", $args{disabled} = 1) if delete $args{disabled}; my $fctx = $ctx->special_context('form'); $fctx->mark_checkbox($name) if $fctx; if ($fctx && defined $fctx->get($name)) { if (grep (/^\Q$value\E$/, $fctx->get($name))) { $args{checked} = 'checked'; } else { delete $args{checked}; } } else { $args{checked} = 'checked' if eval_button_condition(@_); } return tag('input', \%args); }
sub eval_button_condition { return $_[0] if @_ == 1; if (@_) { my @expr = @_; my $code =Communiware::Expr->new(atomparser=>\&Communiware::Expr::quote)->compile_expr(\@expr); return 1 if eval($code); } return ''; }
sub radio_input { my $ctx = shift; my $name = shift; my $value = shift; my $opts = pop; my %args = (name => $ctx->prefix . $name, value => $value, type => 'radio', %$opts); ($args{onclick} = "return false;", $args{disabled} = 1) if delete $args{disabled}; my $fctx;
if (($fctx = $ctx->special_context('form')) && defined $fctx->get($name)) { $args{checked} = 'checked' if $fctx->get($name) eq $value; } else { $args{checked} = 'checked' if eval_button_condition(@_); } return tag('input', \%args); }
sub radio_group {
my $ctx = shift;
my $opts = pop || {};
my $name = shift;
my ($choices, $name_attr, $value_attr, $default) = prepare_group_list($ctx, 'p', @_);
if ( !$choices ) {
return $ctx->get('DEBUG')
? html_error_out('Cannot get list of choices!')
: '';
}
my $columns = delete($opts->{columns}) || 2;
my @inputs;
my ($fctx) = $ctx->special_context('form');
$default = $fctx->get($name)
if $fctx && defined $fctx->get($name);
$default = $choices->[0]{$name_attr} unless length($default);
my $tagname = $ctx->prefix()
. $name;
return make_table(
$columns,
map(tag(
'input',
{
type => 'radio',
name => $tagname,
'value' => $_->{$name_attr},
($default eq $_->{$name_attr} ? ('checked' => 'checked') : ()), %$opts
}
)
. $_->{$value_attr},
@$choices)
);
}
sub make_table { my $columns = shift; my $out .= '<table>'; my $rows = int(($#_ + $columns) / $columns); for (my $i = 0 ; $i < @_ ; $i += $columns) { $out .= ``<tr>''; for (my $j = 0 ; $j < $columns ; $j++) { last if $i + $j >= @_; $out .= ``<td>'' . ($_[$i + $j] || `` '') . ``</td>''; } $out .= ``</tr>''; } $out .= ``</table>''; return $out; }
sub popupmenu { my $ctx = shift; my $opts = pop; my $name = shift; my ($choices, $name_attr, $value_attr, $default) = prepare_group_list($ctx, 't', @_); if ( !$choices ) { return $ctx->get('DEBUG') ? html_error_out('Cannot get list of choices!') : ''; } my $fctx; if (($fctx = $ctx->special_context('form')) && defined $fctx->get($name)) { $default = $fctx->get($name); } return popup_menu($ctx->prefix() . $name, $choices, $name_attr, $value_attr, $default, %$opts); }
sub scrollinglist {
my $ctx = shift;
my $opts = pop;
my $name = shift;
my ($choices, $name_attr, $value_attr, $default) = prepare_group_list($ctx, 't', @_);
if ( !$choices ) {
return $ctx->get('DEBUG')
? html_error_out('Cannot get list of choices!')
: '';
}
my $fctx = $ctx->special_context('form');
if ($fctx && defined $fctx->get($name))
{
$default = $fctx->get($name);
}
$opts->{name} = $ctx->prefix . $name;
$opts->{size} = 3 unless $opts->{size};
delete $opts->{multiple} unless $opts->{multiple};
my %to_check = map {$_ => 1} split(/\s*,\s*/, $default);
return tag(
'select', $opts,
join(
``'',
map (tag(
'option',
{ value => $_->{$name_attr}, ($to_check{ $_->{$name_attr} } ? (selected => 'selected') : ()) },
$_->{$value_attr}
),
@$choices)
)
);
}
sub checkbox_group {
my $ctx = shift;
my $opts = pop;
my $name = shift;
my ($choices, $name_attr, $value_attr, $default) = prepare_group_list($ctx, 'p', @_);
if ( !$choices ) {
return $ctx->get('DEBUG')
? html_error_out('Cannot get list of choices!')
: '';
}
my $columns = delete($opts->{columns}) || 2;
my $fctx = $ctx->special_context('form');
if ($fctx && defined $fctx->get($name))
{
$default = $fctx->get($name);
}
$default = '' if !defined $default;
$fctx->mark_checkbox($name)
if $fctx;
$opts->{name} = $ctx->prefix . $name;
delete $opts->{multiple} unless $opts->{multiple};
my %to_check = map {$_ => 1} split(/\s*,\s*/, $default);
return make_table(
$columns,
map(tag(
'input',
{
%$opts,
type => 'checkbox',
value => $_->{$name_attr},
($to_check{ $_->{$name_attr} } ? (checked => 'checked') : ())
}
)
. $_->{$value_attr},
@$choices)
);
}
sub prepare_group_list { my $ctx = shift; my $format = shift; my $default = shift; my $filter = shift; my ($name_attr, $value_attr); my $choices = []; if ($filter eq 'inline') { while (@_) { push @$choices, { 'ITEM_ID' => shift, 'TITLE' => shift }; } $name_attr = 'ITEM_ID'; $value_attr = 'TITLE'; } else { $name_attr = shift; $value_attr = shift; my $default_label = shift; my $sort = shift;
$default_label = defined $default_label ? $default_label: '';
my @sort; @sort = split(/\s*,\s*/, $sort) if defined $sort; my ($sth, $type_info); eval { ($sth, $type_info) = $ctx->prepare_filter($filter, $name_attr, $value_attr, @sort); }; if ($@) {
# FIXME: catch only DB errors - rethrow other! die $@ if !ref $@; $ctx->logger->error($@); return (undef, undef, undef, undef); } $choices = $sth->fetchall_arrayref({}); unless ($choices->[0]{$value_attr}) { for (@$choices) { $_->{$value_attr} = Communiware::Context::Item->new(attr => $_)->evaluate($value_attr); } } if (($type_info->{$value_attr}[0] || '') eq 'RICHTEXT') { $_->{$value_attr} = untag_html($_->{$value_attr}, $format) foreach @$choices; } if (defined $default && !length $default && !length $default_label) { $default = undef; } unshift @$choices, { $name_attr => $default, $value_attr => $default_label } if length($default_label) && !grep(($default eq $_->{$name_attr}), @$choices); } return ($choices, $name_attr, $value_attr, $default); }
sub submit_button { | |
my $ctx = shift; | |
my $name = shift; | |
my $default = shift; | |
my $opts = pop || {}; | |
my %args = (%$opts, type => 'submit', name => $ctx->prefix . $name, value => $default); | |
if ($args{'confirm'}) { | |
$args{onclick} = ``return confirm('$args{confirm}')''; | |
} | |
delete $args{'confirm'}; | |
my $out = ''; | |
if (scalar(@_)) { | |
if ($ctx->prefix) { |
# Form type=special or Post $out = post_cookie($ctx, @_); } else {
# Form type=context my $fctx = $ctx->special_context('form'); if ($fctx && $fctx->get($name)) {
#Form is already sumbitted. We have to set cookies set_cookies($ctx, @_); } } } return tag('input', \%args) . $out; } sub reset_button { my $ctx = shift; my $name = shift; my $value = shift; my $opts = pop; return tag('input', { type => 'reset', 'value' => $value, %$opts }); }
# # Если третий параметр не пуст, запихивает в список, ссылка на который # передана первым параметром, пару второй => третий #
sub post_cookie {
my $ctx = shift;
my @fields = split(``,'', shift);
my $prefix = $ctx->prefix;
my %cookies = map ({ $_ => /\./ ? ``\%$_'' : ``\%$prefix$_'' } @fields);
my $path = cookie_domain($ctx, shift);
my @cookies;
while (my ($name, $expr) = each(%cookies))
{
push @cookies,
make_cookie(
$ctx, $name,
{
-value => $expr,
-expires => '+10y',
-path => $path
}
);
}
s/%25/%/g foreach @cookies;
return join('', map(hidden_field($prefix . ``Set-Cookie'', $_), @cookies));
}
sub set_cookies { my $ctx = shift; my @fields = split(``,'', shift); my $path = cookie_domain($ctx, shift); for (@fields) { $ctx->document->add_header( 'Set-Cookie', make_cookie( $ctx, $_, { -path => $path, -value => $ctx->get($_), -expires => ``+10y'' } ) ); } }
# # Преобразовывает коммуниверную специйфкацию области действия куки # в путь, соответствующиий RFC 2616 #
sub cookie_domain {
my $ctx = shift;
my $domain = shift || 'site';
my $path;
if ($domain eq 'site') {
$path = $ctx->PREFIX('URL');
}
elsif ($domain eq 'item') {
$path = $ctx->PREFIX('URL')
. ``/'' . $ctx->special_context('document_item')->id;
}
elsif ($domain eq 'exact') {
$path = Apache->request->uri;
}
elsif ($domain eq 'server') {
$path = '/';
}
else {
die(``[Invalid domain '$domain']'');
return ``'';
}
return $path;
}
# # Определяет, был ли атрибут, указанный в качестве параметра, # получен из куки, или из формы #
sub from_cookie { my $name = shift; my @found = grep {$name eq $_} @Communiware::CookieArgs; return scalar(@found); }
1;