| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # -*- perl -*- | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # $Id: Navigation.pm,v 1.20 2004/03/25 11:56:24 eserte Exp $ | 
| 5 |  |  |  |  |  |  | # Author: Slaven Rezic | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | # Copyright (C) 2002 Online Office Berlin. All rights reserved. | 
| 8 |  |  |  |  |  |  | # Copyright (C) 2002 Slaven Rezic. | 
| 9 |  |  |  |  |  |  | # This is free software; you can redistribute it and/or modify it under the | 
| 10 |  |  |  |  |  |  | # terms of the GNU General Public License, see the file COPYING. | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | # | 
| 13 |  |  |  |  |  |  | # Mail: slaven@rezic.de | 
| 14 |  |  |  |  |  |  | # WWW:  http://we-framework.sourceforge.net | 
| 15 |  |  |  |  |  |  | # | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | package WE_Frontend::Plugin::Navigation; | 
| 18 | 1 |  |  | 1 |  | 6 | use base qw(Template::Plugin); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 82 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 1 |  |  | 1 |  | 5 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 30 |  | 
| 21 | 1 |  |  | 1 |  | 5 | use vars qw($VERSION); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 2331 |  | 
| 22 |  |  |  |  |  |  | $VERSION = sprintf("%d.%02d", q$Revision: 1.20 $ =~ /(\d+)\.(\d+)/); | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | require WE_Frontend::Plugin::Navigation::Object; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 NAME | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | WE_Frontend::Plugin::Navigation - utilities for navigation creation | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | my $t = Template->new({PLUGIN_BASE => "WE_Frontend::Plugin"}); | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | [% USE Navigation %] | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | C is a C plugin to | 
| 39 |  |  |  |  |  |  | make the creation of navigations based on objects in a C | 
| 40 |  |  |  |  |  |  | database easier. The C object | 
| 41 |  |  |  |  |  |  | corresponds somewhat to the C database. Most of the | 
| 42 |  |  |  |  |  |  | methods described below return | 
| 43 |  |  |  |  |  |  | L objects, which correspond | 
| 44 |  |  |  |  |  |  | to L objects. | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | =head2 METHODS | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =over | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =item new | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | This method is normally not called directly, but only as part of the | 
| 53 |  |  |  |  |  |  | C | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | [% USE Navigation %] | 
| 56 |  |  |  |  |  |  | [% USE n = Navigation %] | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | You can pass the named parameter C to set the object id for | 
| 59 |  |  |  |  |  |  | this context. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | [% USE n = Navigation(objid = 10) %] | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | Or you can use the C parameter: | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | [% USE n = Navigation(name = "rootcollection") %] | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | You can pass the named parameter C to set the object id for | 
| 68 |  |  |  |  |  |  | this context. | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | When calling the process() method of C the value for | 
| 71 |  |  |  |  |  |  | C (a reference to the L database) should be set or | 
| 72 |  |  |  |  |  |  | C (a reference to the L database) XXX rootdb prefered if content access. | 
| 73 |  |  |  |  |  |  | Also, if C is not set in the C | 
| 74 |  |  |  |  |  |  | supplied to the process() method. | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | my $t = Template->new( | 
| 77 |  |  |  |  |  |  | {PLUGIN_BASE => "WE_Frontend::Plugin"} | 
| 78 |  |  |  |  |  |  | ); | 
| 79 |  |  |  |  |  |  | $t->process( | 
| 80 |  |  |  |  |  |  | \$template, | 
| 81 |  |  |  |  |  |  | { | 
| 82 |  |  |  |  |  |  | objdb      => $objdb, | 
| 83 |  |  |  |  |  |  | rootdb     => $rootdb, | 
| 84 |  |  |  |  |  |  | objid      => $objid, | 
| 85 |  |  |  |  |  |  | config     => $c, | 
| 86 |  |  |  |  |  |  | langstring => \&WE::Util::LangString::langstring | 
| 87 |  |  |  |  |  |  | }, | 
| 88 |  |  |  |  |  |  | \$output | 
| 89 |  |  |  |  |  |  | ); | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | The return value of the C | 
| 92 |  |  |  |  |  |  | in the examples above) is a L | 
| 93 |  |  |  |  |  |  | of the current object supplied with the C key. | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | =cut | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | sub new { | 
| 98 | 0 |  |  | 0 | 1 |  | my($class, $context, $params) = @_; | 
| 99 | 0 |  | 0 |  |  |  | $params ||= {}; | 
| 100 | 0 |  | 0 |  |  |  | my $rootdb = $params->{rootdb} || eval { $context->stash->get("rootdb") }; | 
| 101 | 0 |  | 0 |  |  |  | my $objdb = $params->{objdb} || eval { $context->stash->get("objdb") }; | 
| 102 | 0 | 0 | 0 |  |  |  | if (!$objdb && $rootdb) { | 
| 103 | 0 |  |  |  |  |  | $objdb = $rootdb->ObjDB; | 
| 104 |  |  |  |  |  |  | } | 
| 105 | 0 | 0 |  |  |  |  | if (!$objdb) { | 
| 106 | 0 | 0 |  |  |  |  | if ($rootdb) { | 
| 107 | 0 |  |  |  |  |  | return $class->error("The required parameter rootdb is defined, but its member ObjDB is not defined"); | 
| 108 |  |  |  |  |  |  | } else { | 
| 109 | 0 |  |  |  |  |  | return $class->error("The required parameter objdb and/or rootdb is not defined"); | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 | 0 |  |  |  |  |  | my $objid = $params->{objid}; | 
| 113 | 0 | 0 |  |  |  |  | if (!defined $objid) { | 
| 114 | 0 | 0 |  |  |  |  | if (defined $params->{name}) { | 
| 115 | 0 |  |  |  |  |  | $objid = $objdb->name_to_objid($params->{name}); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  | } | 
| 118 | 0 | 0 |  |  |  |  | if (!defined $objid) { | 
| 119 | 0 |  |  |  |  |  | $objid = eval { $context->stash->get("objid") }; | 
|  | 0 |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | } | 
| 121 | 0 |  |  |  |  |  | my $self = { | 
| 122 |  |  |  |  |  |  | Context => $context, | 
| 123 |  |  |  |  |  |  | RootDB => $rootdb, | 
| 124 |  |  |  |  |  |  | ObjDB => $objdb, | 
| 125 |  |  |  |  |  |  | ObjID => $objid, | 
| 126 |  |  |  |  |  |  | POCache => {}, # for pathobjects_with_cache | 
| 127 |  |  |  |  |  |  | }; | 
| 128 | 0 |  |  |  |  |  | bless $self, $class; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | =item ancestors([[objid = id | name = name], fromlevel => level, tolevel => level, restrict = restrictmethod]) | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | Return a list of ancestors of the current object. The oldest | 
| 134 |  |  |  |  |  |  | (top-most) ancestor is first in the list. If C is given, then | 
| 135 |  |  |  |  |  |  | return the ancestors for the object with this object identifier. If | 
| 136 |  |  |  |  |  |  | C and/or C are given, then restrict the ancestor | 
| 137 |  |  |  |  |  |  | list for these levels. The topmost level is numbered with 1. The root | 
| 138 |  |  |  |  |  |  | itself is numbered with 0, this can be used for a "home" link on top | 
| 139 |  |  |  |  |  |  | of the list. The list may be restricted by specifying C. If | 
| 140 |  |  |  |  |  |  | tolevel is less than fromlevel, then an empty list is returned. | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | =cut | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub ancestors { | 
| 145 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 146 | 0 | 0 |  |  |  |  | my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; | 
| 147 | 0 |  |  |  |  |  | my @l = $self->{ObjDB}->pathobjects_with_cache($self->current_id($params), undef, $self->{POCache}); | 
| 148 | 0 |  |  |  |  |  | pop @l; # delete last in list (the object itself); | 
| 149 | 0 |  |  |  |  |  | $self->objify_list(\@l); | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 0 | 0 |  |  |  |  | if (defined $params->{fromlevel}) { | 
|  |  | 0 |  |  |  |  |  | 
| 152 | 0 | 0 |  |  |  |  | if (defined $params->{tolevel}) { | 
| 153 | 0 |  |  |  |  |  | @l = @l[$params->{fromlevel} .. $params->{tolevel}]; | 
| 154 |  |  |  |  |  |  | } else { | 
| 155 | 0 |  |  |  |  |  | @l = @l[$params->{fromlevel} .. $#l]; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  | } elsif (defined $params->{tolevel}) { | 
| 158 | 0 |  |  |  |  |  | @l = @l[0 .. $params->{tolevel}]; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 0 |  |  |  |  |  | $self->restrict($params, \@l); | 
| 162 | 0 |  |  |  |  |  | [@l]; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =item parent([[objid = id | name = name]]) | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | Return the parent of the current object, or of the object with id | 
| 168 |  |  |  |  |  |  | C. Note that it is possible in the C database to | 
| 169 |  |  |  |  |  |  | have more than one parent, nevertheless only one parent is returned. | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | =cut | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub parent { | 
| 174 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 175 | 0 | 0 |  |  |  |  | my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; | 
| 176 | 0 |  |  |  |  |  | my $obj = $self->current_object($params); | 
| 177 | 0 |  |  |  |  |  | my $objdb = $self->{ObjDB}; | 
| 178 | 0 |  |  |  |  |  | my(@l) = ($objdb->parents($obj))[0]; | 
| 179 | 0 |  |  |  |  |  | $self->objify_list(\@l); | 
| 180 | 0 |  |  |  |  |  | $self->restrict($params, \@l); | 
| 181 | 0 |  |  |  |  |  | $l[0]; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | =item level([[objid = id | name = name]]) | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | Return the level of the current object, or of the object with id | 
| 187 |  |  |  |  |  |  | C. The root of the site has level = 0. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =cut | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | sub level { | 
| 192 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 193 | 0 |  |  |  |  |  | scalar @{ $self->ancestors(@_) }; | 
|  | 0 |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =item toplevel_children([sort = sortmethod, restrict = restrictmethod]) | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Return a list of sorted toplevel children. Normally, the sequence | 
| 199 |  |  |  |  |  |  | order is used but the sorting can be changed by specifying C. | 
| 200 |  |  |  |  |  |  | The list may be restricted by specifying C. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =cut | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub toplevel_children { | 
| 205 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 206 | 0 | 0 |  |  |  |  | my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; | 
| 207 | 0 |  |  |  |  |  | $params->{'level'} = 1; | 
| 208 | 0 |  |  |  |  |  | $self->siblings($params); | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =item siblings([[objid = id | name = name], level = level, sort = sortmethod, restrict => restrictmethod]) | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | Get the siblings of the current object, or of the object with id | 
| 214 |  |  |  |  |  |  | C. The siblings are sorted by the sortmethod in C and | 
| 215 |  |  |  |  |  |  | can be restricted with C. | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | If C is specified, the siblings of the ancestor of the current | 
| 218 |  |  |  |  |  |  | object in the specified level are returned. The level can also be | 
| 219 |  |  |  |  |  |  | specified as a negative number; this means how many levels up from the | 
| 220 |  |  |  |  |  |  | current position should be used. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | =cut | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | sub siblings { | 
| 225 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 226 | 0 | 0 |  |  |  |  | my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; | 
| 227 | 0 |  |  |  |  |  | my $objid = $self->current_id($params); | 
| 228 | 0 |  |  |  |  |  | my $objdb = $self->{ObjDB}; | 
| 229 | 0 |  |  |  |  |  | my $pid; | 
| 230 |  |  |  |  |  |  | my @l; | 
| 231 | 0 | 0 |  |  |  |  | if (defined $params->{level}) { | 
| 232 | 0 |  |  |  |  |  | my @ancestors = $objdb->pathobjects_with_cache($objid, undef, $self->{POCache}); | 
| 233 | 0 | 0 |  |  |  |  | if ($params->{level} =~ /^\d/) { | 
|  |  | 0 |  |  |  |  |  | 
| 234 | 0 |  |  |  |  |  | $pid = $ancestors[$params->{level}-1]; # XXX -1 ??? | 
| 235 |  |  |  |  |  |  | } elsif ($params->{level} =~ /^-(\d+)$/) { | 
| 236 | 0 | 0 |  |  |  |  | if (-($params->{level}-2) > scalar @ancestors + 1) { | 
|  |  | 0 |  |  |  |  |  | 
| 237 | 0 |  |  |  |  |  | return $self->error("Level above root object"); | 
| 238 |  |  |  |  |  |  | } elsif (-($params->{level}-2) == scalar @ancestors + 1) { | 
| 239 |  |  |  |  |  |  | # the root object itself | 
| 240 | 0 |  |  |  |  |  | @l = $ancestors[0]; | 
| 241 | 0 |  |  |  |  |  | $self->objify_list(\@l); | 
| 242 | 0 |  |  |  |  |  | $self->restrict($params, \@l); | 
| 243 |  |  |  |  |  |  | # no sorting necessary :-) | 
| 244 | 0 |  |  |  |  |  | return @l; | 
| 245 |  |  |  |  |  |  | } else { | 
| 246 | 0 |  |  |  |  |  | $pid = $ancestors[$params->{level}-2] | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  | } else { | 
| 249 | 0 |  |  |  |  |  | return $self->error("Invalid level specifier: $params->{level}"); | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  | } else { | 
| 252 | 0 |  |  |  |  |  | $pid = ($objdb->parent_ids($objid))[0]; | 
| 253 |  |  |  |  |  |  | } | 
| 254 | 0 |  |  |  |  |  | @l = $objdb->children($pid); | 
| 255 | 0 |  |  |  |  |  | $self->objify_list(\@l); | 
| 256 | 0 |  |  |  |  |  | $self->restrict($params, \@l); | 
| 257 | 0 |  |  |  |  |  | $self->sort($params, \@l); | 
| 258 | 0 |  |  |  |  |  | [@l]; | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | =item children([[objid = id | name = name], sort = sortmethod, restrict => restrictmethod]) | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | Get the children of the current object, or of the object with id | 
| 264 |  |  |  |  |  |  | C. The children are sorted by the sortmethod in C and | 
| 265 |  |  |  |  |  |  | can be restricted with C. | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | =cut | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | sub children { | 
| 270 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 271 | 0 | 0 |  |  |  |  | my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; | 
| 272 | 0 |  |  |  |  |  | my $obj = $self->current_object($params); | 
| 273 | 0 |  |  |  |  |  | my @l = $self->{ObjDB}->children($obj); | 
| 274 | 0 |  |  |  |  |  | $self->objify_list(\@l); | 
| 275 | 0 |  |  |  |  |  | $self->restrict($params, \@l); | 
| 276 | 0 |  |  |  |  |  | $self->sort($params, \@l); | 
| 277 | 0 |  |  |  |  |  | [@l]; | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | =item siblings_or_children([...]); | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | Often, siblings are used if the object is a document and children are | 
| 283 |  |  |  |  |  |  | used if the object is a folder. This convenience method uses the | 
| 284 |  |  |  |  |  |  | appropriate method. The arguments are the same as in C or | 
| 285 |  |  |  |  |  |  | C. | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | =cut | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | sub siblings_or_children { | 
| 290 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 291 | 0 | 0 |  |  |  |  | my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; | 
| 292 | 0 |  |  |  |  |  | my $obj = $self->current_object($params); | 
| 293 | 0 | 0 |  |  |  |  | if ($obj->is_doc) { | 
| 294 | 0 |  |  |  |  |  | $self->siblings($params); | 
| 295 |  |  |  |  |  |  | } else { | 
| 296 | 0 |  |  |  |  |  | $self->children($params); | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | =item restrict($params, $listref) | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | Internal method to restrict the passed array reference according to | 
| 303 |  |  |  |  |  |  | the C<<$params->{restrict}>> subroutine. | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | The value of the C parameter should be the name of a method | 
| 306 |  |  |  |  |  |  | in the C class. The object is | 
| 307 |  |  |  |  |  |  | accepted if the returned value is true. Example for an user-defined | 
| 308 |  |  |  |  |  |  | method (although subclassing would be the cleaner solution): | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | package WE_Frontend::Plugin::Navigation::Object; | 
| 311 |  |  |  |  |  |  | sub my_restrict { | 
| 312 |  |  |  |  |  |  | my $o = shift; | 
| 313 |  |  |  |  |  |  | # restrict to objects with Id less than 5 | 
| 314 |  |  |  |  |  |  | $o->o->Id < 5; | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | =cut | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | sub restrict { | 
| 320 | 0 |  |  | 0 | 1 |  | my($self, $params, $listref) = @_; | 
| 321 | 0 |  |  |  |  |  | my $sub = $params->{restrict}; | 
| 322 | 0 | 0 | 0 |  |  |  | return if !$sub || !@$listref; | 
| 323 | 0 |  |  |  |  |  | @$listref = grep { $_->$sub() } @$listref; | 
|  | 0 |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =item sort($params, $listref) | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | Internal method to sort the passed array reference according to | 
| 329 |  |  |  |  |  |  | the C<<$params->{sort}>> subroutine. | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | The value of the C parameter should be the name of a method in | 
| 332 |  |  |  |  |  |  | the C class. This method takes to | 
| 333 |  |  |  |  |  |  | arguments C<$a> and C<$b>, both | 
| 334 |  |  |  |  |  |  | C objects which should be | 
| 335 |  |  |  |  |  |  | compared. The returned value should be -1, 0, or 1, just as in the | 
| 336 |  |  |  |  |  |  | C function. Example for an user-defined method (although | 
| 337 |  |  |  |  |  |  | subclassing would be the cleaner solution): | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | package WE_Frontend::Plugin::Navigation; | 
| 340 |  |  |  |  |  |  | sub my_sort { | 
| 341 |  |  |  |  |  |  | my($self, $a, $b) = @_; | 
| 342 |  |  |  |  |  |  | # sort by title | 
| 343 |  |  |  |  |  |  | WE::Util::LangString::langstring($a->o->Title) cmp WE::Util::LangString::langstring($b->o->Title); | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | =cut | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | sub sort { | 
| 349 | 0 |  |  | 0 | 1 |  | my($self, $params, $listref) = @_; | 
| 350 | 0 |  |  |  |  |  | my $sub = $params->{sort}; | 
| 351 | 0 | 0 | 0 |  |  |  | return if !$sub || !@$listref; | 
| 352 | 0 |  |  |  |  |  | @$listref = sort { $self->$sub($a,$b) } @$listref; | 
|  | 0 |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | =item current_object([[objid = id | name = name]]) | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | Return the current active object as a C object. See also the | 
| 358 |  |  |  |  |  |  | C method. | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | =cut | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | sub current_object { | 
| 363 | 0 |  |  | 0 | 1 |  | my($self, $params) = @_; | 
| 364 | 0 |  |  |  |  |  | my $id = $self->current_id($params); | 
| 365 |  |  |  |  |  |  | # XXX check for error? | 
| 366 | 0 |  |  |  |  |  | my $objdb = $self->{ObjDB}; | 
| 367 | 0 |  |  |  |  |  | my $obj = $objdb->get_object($id); | 
| 368 | 0 | 0 |  |  |  |  | if (!$obj) { | 
| 369 | 0 |  |  |  |  |  | return $self->error("Can't get object with id <$id> from database <" . $objdb->DBFile . ">"); | 
| 370 |  |  |  |  |  |  | } | 
| 371 | 0 |  |  |  |  |  | $obj; | 
| 372 |  |  |  |  |  |  | } | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | =item current_id([[objid = id | name = name]]) | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | Return the current active id. The object is identified in this order: | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | =over | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =item C in this method call | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | =item C in this method call | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | =item C parameter in the C C | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | =item C template variable (as specified in the C<< | 
| 387 |  |  |  |  |  |  | Template->new >> call) | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | =back | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | =cut | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | sub current_id { | 
| 394 | 0 |  |  | 0 | 1 |  | my($self, $params) = @_; | 
| 395 | 0 |  |  |  |  |  | my $id; | 
| 396 | 0 | 0 |  |  |  |  | if (defined $params->{objid}) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 397 | 0 |  |  |  |  |  | $id = $params->{objid}; | 
| 398 |  |  |  |  |  |  | } elsif (defined $params->{name}) { | 
| 399 | 0 |  |  |  |  |  | $id = $self->{ObjDB}->name_to_objid($params->{name}); | 
| 400 |  |  |  |  |  |  | } elsif (defined $self->{ObjID}) { | 
| 401 | 0 |  |  |  |  |  | $id = $self->{ObjID}; | 
| 402 |  |  |  |  |  |  | } | 
| 403 | 0 | 0 |  |  |  |  | if (!defined $id) { | 
| 404 | 0 |  |  |  |  |  | return $self->error("No object id defined. Please define it in either: | 
| 405 |  |  |  |  |  |  | * as an objid parameter in the current method call | 
| 406 |  |  |  |  |  |  | * as an name parameter (with an existing name) in the current method call | 
| 407 |  |  |  |  |  |  | * as an objid parameter in the USE directive | 
| 408 |  |  |  |  |  |  | * as an objid template variable | 
| 409 |  |  |  |  |  |  | "); | 
| 410 |  |  |  |  |  |  | } | 
| 411 | 0 |  |  |  |  |  | $id; | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | =item self([[objid = id | name = name]]) | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | Return the current active object as a B<...::Navigation::Object> object. | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | =cut | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | sub self { | 
| 421 | 0 |  |  | 0 | 1 |  | my($self, $params) = @_; | 
| 422 | 0 |  |  |  |  |  | my $class = $self->Object; | 
| 423 | 0 |  |  |  |  |  | $class->new($self->current_object($params), $self); | 
| 424 |  |  |  |  |  |  | } | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =item get_object([[objid = id | name = name]]) | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | This is an alias for B, but uses a more "logical" name if an | 
| 429 |  |  |  |  |  |  | object is retrieved by id or name. | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =cut | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | *get_object = \&self; | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | =item is_self([$testobj | $testobjid], [[objid = id | name = name]]) | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | Return true if the given C<$testobj> (object) or C<$testobjid> (id) is | 
| 438 |  |  |  |  |  |  | the current object. You can pass another C instead of the | 
| 439 |  |  |  |  |  |  | current object. =cut | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =cut | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | sub is_self { | 
| 444 | 0 |  |  | 0 | 1 |  | my($self, $id, $params) = @_; | 
| 445 | 0 |  |  |  |  |  | $self->idify_params($id); | 
| 446 | 0 |  |  |  |  |  | my $current_id = $self->current_id($params); | 
| 447 | 0 |  |  |  |  |  | $id eq $current_id; | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | =item equals([$testobj | $testobjid], [objid = id | name = name]) | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | The same as C, only that either C or C are | 
| 453 |  |  |  |  |  |  | mandatory. | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | Example: | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | [% IF n.equals(testobjid, objid = otherobjid) %] | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | =cut | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | sub equals { | 
| 462 | 0 |  |  | 0 | 1 |  | my($self, $id, $params) = @_; | 
| 463 | 0 | 0 | 0 |  |  |  | if (!exists $params->{objid} && !exists $params->{name}) { | 
| 464 | 0 |  |  |  |  |  | die "Either objid or name are mandatory for the equals method"; | 
| 465 |  |  |  |  |  |  | } | 
| 466 | 0 |  |  |  |  |  | $self->is_self($id, $params); | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | =item is_ancestor([$testobj | $testobjid], [objid => id]) | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | Return true if the given C<$testobj> (object) or C<$testobjid> (id) is | 
| 472 |  |  |  |  |  |  | an ancestor of the current object. You can pass another C | 
| 473 |  |  |  |  |  |  | instead of the current object. The current object is not considered an | 
| 474 |  |  |  |  |  |  | ancestor of itself. | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | =cut | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | sub is_ancestor { | 
| 479 | 0 |  |  | 0 | 1 |  | my($self, $objid, $params) = @_; | 
| 480 | 0 |  |  |  |  |  | $self->idify_params($objid); | 
| 481 | 0 |  |  |  |  |  | my $current_id = $self->current_id($params); | 
| 482 | 0 | 0 |  |  |  |  | return 0 if $objid eq $current_id; | 
| 483 | 0 |  |  |  |  |  | for my $o ($self->{ObjDB}->pathobjects_with_cache($current_id, undef, $self->{POCache})) { | 
| 484 | 0 | 0 |  |  |  |  | return 1 if ($objid eq $o->Id); | 
| 485 |  |  |  |  |  |  | } | 
| 486 | 0 |  |  |  |  |  | 0; | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =item object_by_name($name) | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | Return an object by C<$name>. | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =cut | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | sub object_by_name { | 
| 496 | 0 |  |  | 0 | 1 |  | my($self, $name) = @_; | 
| 497 | 0 |  |  |  |  |  | my $id = $self->{ObjDB}->name_to_objid($name); | 
| 498 | 0 | 0 |  |  |  |  | if (defined $id) { | 
| 499 | 0 |  |  |  |  |  | return $self->self({objid => $id}); | 
| 500 |  |  |  |  |  |  | } | 
| 501 | 0 |  |  |  |  |  | return $self->error("Can't get object by name $name"); | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | =item Root | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | Return reference to root database. | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | =cut | 
| 509 |  |  |  |  |  |  |  | 
| 510 | 0 |  |  | 0 | 1 |  | sub Root { $_[0]->{ObjDB}->Root } | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | =item ObjDB | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | Return reference to the object database. | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | =cut | 
| 517 |  |  |  |  |  |  |  | 
| 518 | 0 |  |  | 0 | 1 |  | sub ObjDB { $_[0]->{ObjDB} } | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | =item Object | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | Return the class name for the navigation objects. This can be | 
| 523 |  |  |  |  |  |  | overridden in inherited classes. | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | =cut | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | sub Object { | 
| 528 | 0 |  |  | 0 | 1 |  | "WE_Frontend::Plugin::Navigation::Object"; | 
| 529 |  |  |  |  |  |  | } | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | =back | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =head2 MEMBERS | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | Remember that there is no visible distinction in the Template-Toolkit | 
| 536 |  |  |  |  |  |  | between accessing members and methods. | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | =over | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | =item Context | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | The C context. | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | =item ObjDB | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | A reference to the object database (C). | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | =back | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | =head2 INTERNAL METHODS | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =over | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | =item objify_list($listref) | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | Internal method to create from a list of C objects a list of | 
| 557 |  |  |  |  |  |  | Navigation objects (see the C | 
| 558 |  |  |  |  |  |  | C<$listref> will be changed. | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | =cut | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | sub objify_list { | 
| 563 | 0 |  |  | 0 | 1 |  | my($self, $objlistref) = @_; | 
| 564 | 0 |  |  |  |  |  | my $class = $self->Object; | 
| 565 | 0 |  |  |  |  |  | @$objlistref = map { $class->new($_, $self) } @$objlistref; | 
|  | 0 |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | } | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =item objectify_params($obj_or_id, $obj_or_id, ...) | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | Turn the given arguments from an object id or C object into | 
| 571 |  |  |  |  |  |  | an C object. | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | =cut | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | sub objectify_params { | 
| 576 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 577 | 0 |  |  |  |  |  | my $class = $self->Object; | 
| 578 | 0 |  |  |  |  |  | my $objdb = $self->{ObjDB}; | 
| 579 | 0 |  |  |  |  |  | for (@_) { | 
| 580 | 0 | 0 |  |  |  |  | if (/^\d+$/) { # treat as object id | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 581 | 0 |  |  |  |  |  | $_ = $objdb->get_object($_); | 
| 582 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($_, $class)) { | 
| 583 |  |  |  |  |  |  | # do nothing | 
| 584 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($_, "WE::Obj")) { | 
| 585 | 0 |  |  |  |  |  | $_ = $class->new($_, $self); | 
| 586 |  |  |  |  |  |  | } else { | 
| 587 | 0 |  |  |  |  |  | warn "Can't interpret $_ in objectify_params"; | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  | } | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | =item idify_params($obj_or_id, ....) | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | Turn the given arguments from an object id or C object into | 
| 595 |  |  |  |  |  |  | an object id. | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | =cut | 
| 598 |  |  |  |  |  |  |  | 
| 599 |  |  |  |  |  |  | sub idify_params { | 
| 600 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 601 | 0 |  |  |  |  |  | my $class = $self->Object; | 
| 602 | 0 |  |  |  |  |  | my $objdb = $self->{ObjDB}; | 
| 603 | 0 |  |  |  |  |  | for (@_) { | 
| 604 | 0 | 0 |  |  |  |  | if (/^\d+$/) { # treat as object id | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | # do nothing | 
| 606 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($_, $class)) { | 
| 607 | 0 |  |  |  |  |  | $_ = $_->o->Id; | 
| 608 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($_, "WE::Obj")) { | 
| 609 | 0 |  |  |  |  |  | $_ = $_->Id; | 
| 610 |  |  |  |  |  |  | } else { | 
| 611 | 0 |  |  |  |  |  | warn "Can't interpret $_ in idify_params"; | 
| 612 |  |  |  |  |  |  | } | 
| 613 |  |  |  |  |  |  | } | 
| 614 |  |  |  |  |  |  | } | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | # hmmm... the default error() method does not throw an exception | 
| 617 |  |  |  |  |  |  | sub error { | 
| 618 | 0 |  |  | 0 | 1 |  | require Carp; | 
| 619 | 0 |  |  |  |  |  | Carp::confess($_[1]); | 
| 620 |  |  |  |  |  |  | } | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | sub dump { | 
| 623 | 0 |  |  | 0 | 0 |  | my($self, $extra) = @_; | 
| 624 | 0 |  |  |  |  |  | my $out = "Dump $self:\n"; | 
| 625 | 0 |  |  |  |  |  | require WE::Util::LangString; | 
| 626 | 0 |  |  |  |  |  | while(my($k,$v) = each %$self) { | 
| 627 | 0 |  |  |  |  |  | $out .= "$k => " . WE::Util::LangString::langstring($v) . "\n"; | 
| 628 |  |  |  |  |  |  | } | 
| 629 | 0 | 0 |  |  |  |  | $out .= "\n$extra" if defined $extra; | 
| 630 | 0 |  |  |  |  |  | $out .= "\n"; | 
| 631 | 0 |  |  |  |  |  | warn $out; | 
| 632 | 0 |  |  |  |  |  | ""; | 
| 633 |  |  |  |  |  |  | } | 
| 634 |  |  |  |  |  |  |  | 
| 635 |  |  |  |  |  |  | # XXX documentation pending | 
| 636 |  |  |  |  |  |  | sub reset_cache { | 
| 637 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 638 | 0 |  |  |  |  |  | $self->{POCache} = {}; | 
| 639 |  |  |  |  |  |  | } | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | ## Debugging aid: | 
| 642 |  |  |  |  |  |  | #  sub DESTROY { | 
| 643 |  |  |  |  |  |  | #      my $self = shift; | 
| 644 |  |  |  |  |  |  | #      warn $self->{ObjDB}->{CH} if defined $self->{ObjDB} && defined $self->{ObjDB}->{CH}; | 
| 645 |  |  |  |  |  |  | #  } | 
| 646 |  |  |  |  |  |  |  | 
| 647 |  |  |  |  |  |  | 1; | 
| 648 |  |  |  |  |  |  |  | 
| 649 |  |  |  |  |  |  | __END__ |