bootmenu —
This tag appears to be affected by, or affects, the following:
Catalog Variables: MV_TREE_TABLE
Global Variables: MV_PAGE
Interchange 5.9.0:
Source: dist/strap/config/bootmenu.tag
Lines: 583
UserTag bootmenu Order name
UserTag bootmenu hasEndTag
UserTag bootmenu AddAttr
UserTag bootmenu Description Display menu using jQuery and standard HTML + CSS.
UserTag bootmenu Documentation <<EOD
Returns a menu using an unordered list with associated Bootstrap-recognizable \
classes (<ul><li>foo</li></ul>). No Javascript is needed for basic functionality.
Call with something like:
[timed-build file="timed/bootmenu" login=1 force=1 minutes=1440][bootmenu \
name="catalog/menu" timed=1][/bootmenu][/timed-build]
(bootmenu's "name" opt requires a DB menu)
or for simple menu:
[bootmenu file="includes/menus/catalog/menu.txt"][/bootmenu]
You can also put a template inside, and also use "transforms" like "logged_in", e.g.:
[bootmenu file="include/menus/catalog/top.txt" class="nav nav-pills pull-right" logged_in=member]
<li class="{HELP_NAME}"><a{PAGE?} href="{PAGE}"{/PAGE?} title="{DESCRIPTION}">{NAME}</a>
[/bootmenu]
To be used with Bootstrap and the Javascript plugin for dropdown menus. \
See http://getbootstrap.com for more.
EOD
UserTag bootmenu Routine <<EOR
my $indicated;
my $last_line;
my $first_line;
my $logical_field;
my %transform = (
nbsp => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
$row->{$_} =~ s/ / /g;
}
return 1;
},
entities => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
$row->{$_} = HTML::Entities::encode_entities($row->{$_});
}
return 1;
},
localize => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
$row->{$_} = errmsg($row->{$_});
}
return 1;
},
first_line => sub {
my ($row, $fields) = @_;
return undef if ref($fields) ne 'ARRAY';
return 1 if $first_line;
my $status;
for(@$fields) {
if(s/^!\s*//) {
$status = $status && ! $row->{$_};
}
else {
$status = $status && $row->{$_};
}
}
return $first_line = $status;
},
last_line => sub {
my ($row, $fields) = @_;
#::logDebug("last_line transform, last_line=$last_line");
return 1 if ref($fields) ne 'ARRAY';
return 0 if $last_line;
my $status;
for(@$fields) {
#::logDebug("last_line transform checking field $_=$row->{$_}");
if(s/^!\s*//) {
$status = ! $row->{$_};
}
else {
$status = $row->{$_};
}
#::logDebug("last_line transform checked field $_=$row->{$_}, status=$status");
last if $status;
}
#::logDebug("last_line transform returning last_line=$status");
$last_line = $status;
#::logDebug("last_line transform returning status=" . ! $status);
return ! $status;
},
first_line => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
if(s/^!\s*//) {
$status = $status && ! $row->{$_};
}
else {
$status = $status && $row->{$_};
}
}
return $status;
},
inactive => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
if(s/^!\s*//) {
$status = $status && $row->{$_};
}
else {
$status = $status && ! $row->{$_};
}
}
return $status;
},
active => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
if(s/^!\s*//) {
$status = $status && ! $row->{$_};
}
else {
$status = $status && $row->{$_};
}
}
return $status;
},
ui_security => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
next if ! length($row->{$_});
$status = $status && Vend::Tags->if_mm('advanced', $row->{$_});
}
return $status;
},
full_interpolate => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
next unless $row->{$_} =~ /\[|__[A-Z]\w+__/;
$row->{$_} = Vend::Interpolate::interpolate_html($row->{$_});
}
return 1;
},
page_class => sub {
my ($row, $fields) = @_;
return 1 unless $row->{indicated};
return 1 if $row->{mv_level};
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
my($f, $c) = split /[=~]+/, $_;
$c ||= $f;
#::logDebug("setting scratch $f to row=$c=$row->{$c}");
$::Scratch->{$f} = $row->{$c};
}
$$indicated = 0;
return 1;
},
menu_group => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
eval {
for(@$fields) {
my($f, $c) = split /[=~]+/, $_;
$c ||= $f;
$status = $status && (
! $row->{$f}
or $CGI::values{$c} =~ /$row->{$f}/i
);
}
};
return $status;
},
superuser => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
$status = $status && (! $row->{$_} or Vend::Tags->if_mm('super'));
}
return $status;
},
items => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
my $nitems = scalar(@{$Vend::Items}) ? 1 : 0;
for(@$fields) {
next if ! length($row->{$_});
$status = $status && (! $nitems ^ $row->{$_});
}
return $status;
},
logged_in => sub {
my ($row, $fields) = @_;
#::logDebug("logged_in... doing:$_, fields=" . ref($fields) . ', ' . uneval($fields));
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
next if ! length($row->{$_});
$status = $status && (! $::Vend::Session->{logged_in} ^ $row->{$_});
}
#::logDebug("logged_in... got here. doing:$_, status=$status, row=$row->{$_}");
return $status;
},
depends_on => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
next if ! $row->{$_};
$status = $status && $CGI::values{$row->{$_}};
}
return $status;
},
exclude_on => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
my $status = 1;
for(@$fields) {
$status = $status && (! $CGI::values{$row->{$_}});
}
return $status;
},
indicator_class => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
my ($indicator,$rev, $last, $status);
my($s,$r) = split /=/, $_;
$rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
$last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
#::logDebug("checking scratch $s=$::Scratch->{$s} eq row=$r=$row->{$r}");
$status = $::Scratch->{$s} eq $row->{$r};
if($rev xor $status) {
$row->{indicated} = 1;
}
last if $last;
}
if($row->{indicated}) {
$indicated = \$row->{indicated};
}
return 1;
},
indicator_profile => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
my ($indicator,$rev, $last, $status);
next unless $indicator = $row->{$_};
$rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
$last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
$status = Vend::Tags->run_profile($indicator);
if($rev xor $status) {
$row->{indicated} = 1;
next unless $last;
}
last if $last;
}
return 1;
},
indicator_page => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
if ($::Scratch->{mv_logical_page} eq $row->{$_}) {
unless(
$::Scratch->{mv_logical_page_used}
and $::Scratch->{mv_logical_page_used}
ne
$row->{$logical_field}
)
{
$row->{indicated} = 1;
$::Scratch->{mv_logical_page_used} = $row->{$logical_field};
last;
}
}
($row->{indicated} = 1, last)
if $Global::Variable->{MV_PAGE} eq $row->{$_}
and ! defined $row->{indicated};
}
return 1;
},
indicator => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
my ($indicator,$rev, $last, $status);
next unless $indicator = $row->{$_};
$rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
$last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
if($indicator =~ /^\s*([-\w.:][-\w.:]+)\s*$/) {
$status = $CGI::values{$1};
}
elsif ($indicator =~ /^\s*`(.*)`\s*$/s) {
$status = Vend::Interpolate::tag_calc($1);
}
elsif ($indicator =~ /\[/s) {
$status = Vend::Interpolate::interpolate_html($indicator);
$status =~ s/\s+//g;
}
if($rev xor $status) {
$row->{indicated} = 1;
}
else {
$row->{indicated} = '';
}
last if $last;
}
return 1;
},
expand_values_form => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
next unless $row->{$_} =~ /\%5b|\[/i;
my @parms = split $Global::UrlSplittor, $row->{$_};
my @out;
for my $p (@parms) {
my ($parm, $val) = split /=/, $p, 2;
$val = unhexify($val);
$val =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/g;
$val =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/g;
$val =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/g;
push @out, join('=', $parm, hexify($val));
}
$row->{$_} = join $Global::UrlJoiner, @out;
}
return 1;
},
expand_values => sub {
my ($row, $fields) = @_;
return 1 if ref($fields) ne 'ARRAY';
for(@$fields) {
next unless $row->{$_} =~ /\[/;
$row->{$_} =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/g;
$row->{$_} =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/g;
$row->{$_} =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/g;
}
return 1;
},
);
sub reset_transforms {
#::logDebug("resetting transforms");
my $opt = shift;
if($opt) {
$logical_field = $opt->{logical_page_field} || 'name';
}
undef $last_line;
undef $first_line;
undef $indicated;
}
sub {
my($name, $opt, $template) = @_;
reset_transforms($opt);
my @transform;
my @ordered_transform = qw/full_interpolate indicator_page page_class \
indicator_class localize entities nbsp/;
my %ordered;
@ordered{@ordered_transform} = @ordered_transform;
for(keys %transform) {
next if $ordered{$_};
next unless $opt->{$_};
my @fields = grep /\S/, split /[\s,\0]+/, $opt->{$_};
$opt->{$_} = \@fields;
#::logDebug("opt $_ = " . uneval(\@fields));
push @transform, $_;
}
for(@ordered_transform) {
next unless $opt->{$_};
my @fields = grep /\S/, split /[\s,\0]+/, $opt->{$_};
$opt->{$_} = \@fields;
push @transform, $_;
}
$opt->{_transform} = \@transform;
#::logDebug("in menu sub main");
my @out;
$template = <<EOF if $template !~ /\S/;
{INDICATOR?}<li class="{INDICATOR}">{/INDICATOR?}
{INDICATOR:}<li class="{BOOT_LI}">
<a{PAGE?} href="{PAGE}"{/PAGE?} title="{DESCRIPTION}" class="{LINK_CLASS}" \
\
{BOOT_CONTENT}>{ICON?}{ICON} {/ICON?}{NAME}{CARET?} {CARET}{/CARET?}</a>
{/INDICATOR:}
EOF
my $top_timeout = $opt->{timeout} || 1000;
my %o = (
start => $opt->{tree_selector} || $opt->{name},
file => $opt->{file},
table => $opt->{table} || $::Variable->{MV_TREE_TABLE} || 'tree',
master => 'parent_fld',
subordinate => 'code',
autodetect => '1',
sort => $opt->{sort} || 'code',
full => '1',
timed => $opt->{timed},
spacing => '4',
_transform => $opt->{_transform},
);
for(@{$opt->{_transform} || []}) {
$o{$_} = $opt->{$_};
}
my $main;
my $rows;
if($opt->{iterator}) {
$o{iterator} = $opt->{iterator};
$main = Vend::Tags->tree(\%o);
$rows = $o{object}{mv_results};
}
else {
Vend::Tags->tree(\%o);
#::logDebug("bootmenu: " . uneval({ ref => \%o }) );
my @o;
for(@{$o{object}{mv_results}}) {
next if $_->{deleted};
for my $tr (@{$o{_transform}}) {
#::logDebug("running transform: $tr, on: " . uneval($_) . ", " . uneval($opt->{$tr}));
my $status = $transform{$tr}->($_, $opt->{$tr});
#::logDebug("transform... status=$status, did: $tr, result: " . uneval($_));
$opt->{next_level} = $_->{mv_level}
if ! $status;
$_->{deleted} = 1 unless $status;
}
if($_->{page} and $_->{page} !~ m{^(\w+:)?/}) {
my $form = $_->{form};
if($form and $form !~ /[\r\n]/) {
$form = join "\n", split $Global::UrlSplittor, $form;
}
$_->{page} = "" if $_->{page} eq 'index';
my $add = ($::Scratch->{mv_add_dot_html} && $_->{page} !~ /\.\w+$/) || 0;
$_->{page} = Vend::Tags->area({
href => $_->{page},
form => $form,
no_count => $o{timed},
add_dot_html => $add,
no_session_id => $o{timed},
auto_format => 1,
}) unless $_->{page} =~ /^#/;
}
push @o, $_ unless $_->{deleted};
}
$rows = \@o;
}
$rows->[-1]{mv_last_row} = 1 if @$rows;
#::logDebug("rows = " . ::uneval({ ref => $rows }) );
$name =~ s|/|_|g;
$opt->{ul_id} ||= $name;
$opt->{class} ||= 'nav navbar-nav';
my $id = $opt->{ul_id} ? q{ id="$opt->{ui_id}"} : '';
my $style = $opt->{style} ? q{ style="$opt->{style}"} : '';
push @out, <<EOF;
<ul$id class="$opt->{class}"$style $opt->{extra}>
EOF
#return Vend::Tags->uneval({ ref => $rows });
## Dropdown classes
my $boot_content = qq| data-toggle="dropdown" role="button" data-target="#"|;
my $boot_class = qq|dropdown-toggle|;
my $boot_li = qq|dropdown|;
my $caret = $opt->{caret} || qq|<b class="caret"></b>|;
my $class = $opt->{class};
my $link_class = $opt->{link_class};
my $li_class = $opt->{li_class};
my $z = 0;
my $last_level = 0;
for my $row (@$rows) {
#Debug("mvlevel:$row->{mv_level} last:$last_level lastrow:$row->{mv_last_row} z:$z");
next if $row->{deleted};
if($row->{mv_children} > 0){
$row->{boot_content} = $boot_content;
$row->{link_class} = "$boot_class $link_class";
$row->{caret} = $caret;
$row->{boot_li} = "$boot_li $li_class";
}
else{
$row->{link_class} = $link_class;
$row->{boot_li} = "$li_class";
}
## Allow for bootstrap icon set
unless ($row->{img_icon} =~ /\.(jpg|gif|png|jpeg)$/i){
$row->{icon} = qq|<i class="$row->{img_icon}"></i>| if $row->{img_icon};
}
my ($in_template, $list_open, $list_close);
if($row->{mv_level} > $last_level) { # new nested list
$list_open = <<EOF;
<ul class="dropdown dropdown-menu">
EOF
}
elsif($row->{mv_level} < $last_level) { # end of nested list
$list_close = <<EOF;
<!-- level:$row->{mv_level} -->
</li>
</ul>
</li>
EOF
my $add_close = <<EOF;
</ul>
EOF
my $level_diff = $last_level - $row->{mv_level};
for(2..$level_diff) {
$list_close .= $add_close;
}
}
elsif ($z) {
$list_close = <<EOF;
</li>
EOF
}
$in_template = $list_close . $list_open . $template;
if($row->{mv_last_row}) {
my $scl = <<EOF;
</li>
EOF
my $cl = <<EOF;
</li>
</ul>
EOF
if ($row->{mv_level} == 1){
$in_template .= $cl;
}
elsif ($row->{mv_level} > 1) {
while($last_level >= 0){
$in_template .= $cl;
$last_level--;
}
}
$in_template .= $scl;
}
push @out, Vend::Tags->uc_attr_list($row, $in_template);
$last_level = $row->{mv_level};
$z++;
}
push @out, <<EOF;
</ul>
EOF
return join "", @out;
}
EOR