voxforge.org
VoxForge Dev

root/Trunk/WebGUI/dev/Asset.pm

Revision 2415, 76.2 kB (checked in by kmaclean, 1 year ago)

add AssetReport? WebGUI wobject

Line 
1 package WebGUI::Asset;
2
3 =head1 LEGAL
4
5  -------------------------------------------------------------------
6   WebGUI is Copyright 2001-2006 Plain Black Corporation.
7  -------------------------------------------------------------------
8   Please read the legal notices (docs/legal.txt) and the license
9   (docs/license.txt) that came with this distribution before using
10   this software.
11  -------------------------------------------------------------------
12   http://www.plainblack.com                     info@plainblack.com
13  -------------------------------------------------------------------
14
15 =cut
16
17 use WebGUI::AssetBranch;
18 use WebGUI::AssetClipboard;
19 use WebGUI::AssetExportHtml;
20 use WebGUI::AssetLineage;
21 use WebGUI::AssetMetaData;
22 use WebGUI::AssetPackage;
23 use WebGUI::AssetTrash;
24 use WebGUI::AssetVersioning;
25 use strict;
26 use Tie::IxHash;
27 use WebGUI::AdminConsole;
28 use WebGUI::Cache;
29 use WebGUI::Form;
30 use WebGUI::HTML;
31 use WebGUI::HTMLForm;
32 use WebGUI::TabForm;
33 use WebGUI::Utility;
34
35 =head1 NAME
36
37 Package WebGUI::Asset
38
39 =head1 DESCRIPTION
40
41 Package to manipulate items in WebGUI's asset system. Replaces Collateral.
42
43 =head1 SYNOPSIS
44
45 An asset is the basic class of content in WebGUI. This handles security, urls, and other basic information common to all content items.
46
47 A lineage is a concatenated series of sequence numbers, each six digits long, that explain an asset's position in its familiy tree. Lineage describes who the asset's ancestors are, how many ancestors the asset has in its family tree (lineage length), and the asset's position (rank) amongst its siblings. In addition, lineage provides enough information about an asset to generate a list of its siblings and descendants.
48
49  use WebGUI::Asset;
50
51 =head1 METHODS
52
53 These methods are available from this class:
54
55 =cut
56
57
58 #-------------------------------------------------------------------
59
60 =head2 addMissing ( url )
61
62 Displays a message to the admin that they have requested a non-existent page and give them an option to create it.
63
64 =head3 url
65
66 The missing URL.
67
68 =cut
69
70 sub addMissing {
71         my $self = shift;
72         my $assetUrl = shift;
73         return undef unless ($self->session->var->isAdminOn);
74         my $ac = $self->getAdminConsole;
75         my $i18n = WebGUI::International->new($self->session, "Asset");
76         my $output = $i18n->get("missing page query");
77         $output .= '<ul>
78                         <li><a href="'.$self->getUrl("func=add;class=WebGUI::Asset::Wobject::Layout;url=".$assetUrl).'">'.$i18n->get("add the missing page").'</a></li>
79                         <li><a href="'.$self->getUrl.'">'.$i18n->get("493","WebGUI").'</a></li>
80                         </ul>';
81         return $ac->render($output);
82 }
83
84 #-------------------------------------------------------------------
85
86 =head2 canAdd ( session, [userId, groupId] )
87
88 Verifies that the user has the privileges necessary to add this type of asset. Return a boolean.
89
90 =head3 session
91
92 The session variable.
93
94 =head3 userId
95
96 Unique hash identifier for a user. If not supplied, current user.
97
98 =head3 groupId
99
100 Only developers extending this method should use this parameter. By default WebGUI will check groups in this order, whichever is defined: Group id assigned in the config file for each asset. Group assigned by the developer in the asset itself if s/he extended this method to do so. The "turn admin on" group which is group id 12.
101
102 =cut
103
104 sub canAdd {
105         my $className = shift;
106         my $session = shift;
107         my $userId = shift || $session->user->userId;
108         my $user = WebGUI::User->new($session, $userId);
109         my $subclassGroupId = shift;
110         my $addPrivs = $session->config->get("assetAddPrivilege");
111         my $groupId = $addPrivs->{$className} || $subclassGroupId || '12';
112         return $user->isInGroup($groupId);
113 }
114
115
116 #-------------------------------------------------------------------
117
118 =head2 canEdit ( [userId] )
119
120 Verifies group and user permissions to be able to edit asset. Returns 1 if owner is userId, otherwise returns the result checking if the user is a member of the group that can edit.
121
122 =head3 userId
123
124 Unique hash identifier for a user. If not supplied, current user.
125
126 =cut
127
128 sub canEdit {
129         my $self = shift;
130         my $userId = shift || $self->session->user->userId;
131         my $user = WebGUI::User->new($self->session, $userId);
132         if ($userId eq $self->get("ownerUserId")) {
133                 return 1;
134         }
135         return $user->isInGroup($self->get("groupIdEdit"));
136 }
137
138
139 #-------------------------------------------------------------------
140
141 =head2 canView ( [userId] )
142
143 Verifies group and user permissions to be able to view asset. Returns 1 if user is owner of asset. Returns 1 if within the visibility date range of the asset AND user in the View group of asset. Otherwise, returns the result of the canEdit.
144
145 Only the owner and the editors can always see the asset, regardless of time/date restrictions on the asset.
146
147 =head3 userId
148
149 Unique hash identifier for a user. If not specified, uses current userId.
150
151 =cut
152
153
154 sub canView {
155         my $self = shift;
156         my $eh = $self->session->errorHandler;
157         my $userId = shift;
158         my $user;
159         if (defined $userId) {
160                 $user = WebGUI::User->new($self->session, $userId);
161         }
162         else {
163                 $user =  $self->session->user;
164                 $userId = $user->userId();
165         }
166         if ($userId eq $self->get("ownerUserId")) {
167                 return 1;
168         } elsif ($user->isInGroup($self->get("groupIdView"))) {
169                 return 1;
170         }
171         return $self->canEdit($userId);
172 }
173
174
175 #-------------------------------------------------------------------
176
177 =head2 checkView ( )
178
179 Returns error messages if a user can't view due to publishing problems, otherwise it sets the cookie and returns undef. This is sort of a hack until we find something better.
180
181 =cut
182
183 sub checkView {
184         my $self = shift;
185         return $self->session->privilege->noAccess() unless $self->canView;
186         my ($var, $http) = $self->session->quick(qw(var http));
187         if ($var->isAdminOn && $self->get("state") =~ /^trash/) { # show em trash
188                 $http->setRedirect($self->getUrl("func=manageTrash"));
189                 return "redirect";
190         } elsif ($var->isAdminOn && $self->get("state") =~ /^clipboard/) { # show em clipboard
191                 $http->setRedirect($self->getUrl("func=manageClipboard"));
192                 return "redirect";
193         } elsif ($self->get("state") ne "published" && $self->get("state") ne "archived") { # tell em it doesn't exist anymore
194                 $http->setStatus("410");
195                 my $notFound = WebGUI::Asset->getNotFound($self->session);
196                 $self->session->asset($notFound);
197                 return $notFound->www_view;
198         }
199         $self->logView();
200         return undef;
201 }
202
203 #-------------------------------------------------------------------
204
205 =head2 definition ( [ definition ] )
206
207 Basic definition of an Asset. Properties, default values. Returns an array reference containing tableName,className,properties
208
209 =head3 definition
210
211 An array reference containing additional information to include with the default definition.
212
213 =cut
214
215 sub definition {
216     my $class = shift;
217     my $session = shift;
218     my $definition = shift || [];
219         my $i18n = WebGUI::International->new($session, "Asset");
220         my %properties;
221         tie %properties, 'Tie::IxHash';
222         %properties = (
223                     title=>{
224                                             tab=>"properties",
225                                             label=>$i18n->get(99),
226                                             hoverHelp=>$i18n->get('99 description'),
227                         fieldType=>'text',
228                         defaultValue=>'Untitled',
229                                             filter=>'fixTitle',
230                     },
231                     menuTitle=>{
232                                             tab=>"properties",
233                                             label=>$i18n->get(411),
234                                             hoverHelp=>$i18n->get('411 description'),
235                                             uiLevel=>1,
236                         fieldType=>'text',
237                                             filter=>'fixTitle',
238                         defaultValue=>undef
239                     },
240                     url=>{
241                                             tab=>"properties",
242                                             label=>$i18n->get(104),
243                                             hoverHelp=>$i18n->get('104 description'),
244                                             uiLevel=>3,
245                         fieldType=>'text',
246                         defaultValue=>undef,
247                                             filter=>'fixUrl'
248                     },
249                                     isHidden=>{
250                                             tab=>"display",
251                                             label=>$i18n->get(886),
252                                             hoverHelp=>$i18n->get('886 description'),
253                                             uiLevel=>6,
254                                             fieldType=>'yesNo',
255                                             defaultValue=>0
256                                         },
257                                     newWindow=>{
258                                             tab=>"display",
259                                             label=>$i18n->get(940),
260                                             hoverHelp=>$i18n->get('940 description'),
261                                             uiLevel=>9,
262                                             fieldType=>'yesNo',
263                                             defaultValue=>0
264                                         },
265                                     encryptPage=>{
266                                             fieldType=>'yesNo',
267                                             tab=>"security",
268                                             label=>$i18n->get('encrypt page'),
269                                             hoverHelp=>$i18n->get('encrypt page description'),
270                                             uiLevel=>6,
271                                             defaultValue=>0
272                                         },
273                     ownerUserId=>{
274                                             tab=>"security",
275                                             label=>$i18n->get(108),
276                                             hoverHelp=>$i18n->get('108 description'),
277                                             uiLevel=>6,
278                         fieldType=>'user',
279                         defaultValue=>'3'
280                     },
281                     groupIdView=>{
282                                             tab=>"security",
283                                             label=>$i18n->get(872),
284                                             hoverHelp=>$i18n->get('872 description'),
285                                             uiLevel=>6,
286                         fieldType=>'group',
287                         defaultValue=>'7'
288                     },
289                     groupIdEdit=>{
290                                             tab=>"security",
291                                             label=>$i18n->get(871),
292                                             excludeGroups=>[1,7],
293                                             hoverHelp=>$i18n->get('871 description'),
294                                             uiLevel=>6,
295                         fieldType=>'group',
296                         defaultValue=>'4'
297                     },
298                     synopsis=>{
299                                             tab=>"meta",
300                                             label=>$i18n->get(412),
301                                             hoverHelp=>$i18n->get('412 description'),
302                                             uiLevel=>3,
303                         fieldType=>'textarea',
304                         defaultValue=>undef
305                     },
306                     extraHeadTags=>{
307                                             tab=>"meta",
308                                             label=>$i18n->get("extra head tags"),
309                                             hoverHelp=>$i18n->get('extra head tags description'),
310                                             uiLevel=>5,
311                         fieldType=>'textarea',
312                         defaultValue=>undef
313                     },
314                                     isPackage=>{
315                                             label=>$i18n->get("make package"),
316                                             tab=>"meta",
317                                             hoverHelp=>$i18n->get('make package description'),
318                                             uiLevel=>7,
319                                             fieldType=>'yesNo',
320                                             defaultValue=>0
321                                         },
322                                     isPrototype=>{
323                                             tab=>"meta",
324                                             label=>$i18n->get("make prototype"),
325                                             hoverHelp=>$i18n->get('make prototype description'),
326                                             uiLevel=>9,
327                                             fieldType=>'yesNo',
328                                             defaultValue=>0
329                                         },
330                                     status=>{
331                                             noFormPost=>1,
332                                             fieldType=>'hidden',
333                                             defaultValue=>'pending'
334                                         },
335                                     assetSize=>{
336                                             noFormPost=>1,
337                                             fieldType=>'hidden',
338                                             defaultValue=>0
339                                         },
340     );
341     push(@{$definition}, {
342             assetName=>$i18n->get("asset"),
343         tableName=>'assetData',
344                 autoGenerateForms=>1,
345         className=>'WebGUI::Asset',
346                 icon=>'assets.gif',
347         properties=>\%properties
348         }
349     );
350     return $definition;
351 }
352
353
354 #-------------------------------------------------------------------
355
356 =head2 DESTROY ( )
357
358 Completely remove an asset from existence.
359
360 =cut
361
362 sub DESTROY {
363         my $self = shift;
364         # something bad happens when the following is enabled, not sure why
365         # must check this out later
366         #$self->{_parent}->DESTROY if (exists $self->{_parent});
367         $self->{_firstChild}->DESTROY if (exists $self->{_firstChild});
368         $self->{_lastChild}->DESTROY if (exists $self->{_lastChild});
369         $self = undef;
370 }
371
372
373 #-------------------------------------------------------------------
374
375 =head2 fixUrl ( string )
376
377 Returns a URL, removing invalid characters and making it unique.
378
379 =head3 string
380
381 Any text string. Most likely will have been the Asset's name or title.
382
383 =cut
384
385 sub fixUrl {
386         my $self = shift;
387         my $url = shift;
388
389         # build a URL from the parent
390         unless ($url) {
391                 $url = $self->getParent->get("url");
392                 $url =~ s/(.*)\..*/$1/;
393                 $url .= '/'.$self->getValue("menuTitle");
394         }
395         $url = $self->session->url->urlize($url);
396
397         # fix urls used by uploads and extras
398         # and those beginning with http
399         my @badUrls = ($self->session->config->get("extrasURL"), $self->session->config->get("uploadsURL"));
400         foreach my $badUrl (@badUrls) {
401                 if ($badUrl =~ /^http/) {
402                         $badUrl =~ s/^http.*\/(.*)$/$1/;
403                 } else {
404                         $badUrl =~ s/^\/(.*)/$1/;
405                 }
406                 if ($url =~ /^$badUrl/) {
407                         $url = "_".$url;
408                 }
409         }
410
411         # urls can't be longer than 250 characters
412         if (length($url) > 250) {
413                 $url = substr($url,220);
414         }
415
416         # remove multiple extensions from the url if there are some
417         while ($url =~ m{^(.*)\.\w+(/.*)$}) {
418                 $url =~ s{^(.*)\.\w+(/.*)$}{$1$2}ig;
419         }
420
421         # add automatic extension if we're supposed to
422         if ($self->session->setting->get("urlExtension") ne "" #don't add an extension if one isn't set
423                 && !($url =~ /\./) #don't add an extension of the url already contains a dot
424                 && $self->get("url") eq $self->getId # only add it if we're creating a new url
425                 ) {
426                 $url .= ".".$self->session->setting->get("urlExtension");
427         }
428
429         # check to see if the url already exists or not, and increment it if it does
430         if ($self->urlExists($self->session, $url, {assetId=>$self->getId})) {
431                 my @parts = split(/\./,$url);
432                 if ($parts[0] =~ /(.*)(\d+$)/) {
433                         $parts[0] = $1.($2+1);
434                 } else {
435                         $parts[0] .= "2";
436                 }
437                 $url = join(".",@parts);
438                 $url = $self->fixUrl($url);
439         }
440         return $url;
441 }
442
443
444 #-------------------------------------------------------------------
445
446 =head2 fixTitle ( string )
447
448 Fixes a title by eliminating HTML from it.
449
450 =head3 string
451
452 Any text string. Most likely will have been the Asset's name or title.
453
454 =cut
455
456 sub fixTitle {
457         my $self = shift;
458         return WebGUI::HTML::filter(shift || $self->getValue("title") || 'Untitled', 'all');
459 }
460
461
462 #-------------------------------------------------------------------
463
464 =head2 get ( [propertyName] )
465
466 Returns a reference to a list of properties (or specified property) of an Asset.
467
468 =head3 propertyName
469
470 Any of the values associated with the properties of an Asset. Default choices are "title", "menutTitle", "synopsis", "url", "groupIdEdit", "groupIdView", "ownerUserId",  and "assetSize".
471
472 =cut
473
474 sub get {
475         my $self = shift;
476         my $propertyName = shift;
477         if (defined $propertyName) {
478                 return $self->{_properties}{$propertyName};
479         }
480         my %copyOfHashRef = %{$self->{_properties}};
481         return \%copyOfHashRef;
482 }
483
484
485
486 #-------------------------------------------------------------------
487
488 =head2 getAdminConsole ( )
489
490 Returns a reference to a WebGUI::AdminConsole object.
491
492 =cut
493
494 sub getAdminConsole {
495         my $self = shift;
496         unless (exists $self->{_adminConsole}) {
497                 $self->{_adminConsole} = WebGUI::AdminConsole->new($self->session,"assets");
498         }
499         $self->{_adminConsole}->setIcon($self->getIcon);
500         return $self->{_adminConsole};
501 }
502
503 #-------------------------------------------------------------------
504
505 =head2 getAssetAdderLinks ( [addToUrl, type] )
506
507 Returns an arrayref that contains a label (name of the class of Asset) and url (url link to function to add the class).
508
509 =head3 addToUrl
510
511 Any text to append to the getAssetAdderLinks URL. Usually name/variable pairs to pass in the url. If addToURL is specified, the character ";" and the text in addToUrl is appended to the returned url.
512
513 =head3 type
514
515 A string indicating which type of adders to return. Defaults to "assets". Choose from "assets", "assetContainers", or "utilityAssets".
516
517 =cut
518
519 sub getAssetAdderLinks {
520         my $self = shift;
521         my $addToUrl = shift;
522         my $type = shift || "assets";
523         my %links;
524         my $classesInType = $self->session->config->get($type);
525         if (ref $classesInType ne "ARRAY") {
526                 $classesInType = [];
527         }
528         foreach my $class (@{$classesInType}) {
529                 next unless $class;
530                 my %properties = (
531                         className=>$class,
532                         dummy=>1
533                 );
534                 my $newAsset = WebGUI::Asset->newByPropertyHashRef($self->session,\%properties);
535                 next unless $newAsset;
536                 my $uiLevel = eval{$newAsset->getUiLevel()};
537                 if ($@) {
538                         $self->session->errorHandler->error("Couldn't get UI level of ".$class.". Root cause: ".$@);
539                         next;
540                 }
541                 next if ($uiLevel > $self->session->user->profileField("uiLevel"));# && !$self->session->user->isInGroup(3));
542                 my $canAdd = eval{$class->canAdd($self->session)};
543                 if ($@) {
544                         $self->session->errorHandler->error("Couldn't determine if user can add ".$class." because ".$@);
545                         next;
546                 }
547                 next unless ($canAdd);
548                 my $label = eval{$newAsset->getName()};
549                 if ($@) {
550                         $self->session->errorHandler->error("Couldn't get the name of ".$class."because ".$@);
551                         next;
552                 }
553                 my $url = $self->getUrl("func=add;class=".$class);
554                 $url = $self->session->url->append($url,$addToUrl) if ($addToUrl);
555                 $links{$label}{url} = $url;
556                 $links{$label}{icon} = $newAsset->getIcon;
557                 $links{$label}{'icon.small'} = $newAsset->getIcon(1);
558         }
559         my $constraint;
560         if ($type eq "assetContainers") {
561                 $constraint = $self->session->db->quoteAndJoin($self->session->config->get("assetContainers"));
562         } elsif ($type eq "utilityAssets") {
563                 $constraint = $self->session->db->quoteAndJoin($self->session->config->get("utilityAssets"));
564         } else {
565                 $constraint = $self->session->db->quoteAndJoin($self->session->config->get("assets"));
566         }
567         if ($constraint) {
568                 my $sth = $self->session->db->read("select asset.className,asset.assetId,assetData.revisionDate from asset left join assetData on asset.assetId=assetData.assetId where assetData.isPrototype=1 and asset.state='published' and asset.className in ($constraint) and assetData.revisionDate=(SELECT max(revisionDate) from assetData where assetData.assetId=asset.assetId) group by assetData.assetId");
569                 while (my ($class,$id,$date) = $sth->array) {
570                         my $asset = WebGUI::Asset->new($self->session,$id,$class,$date);
571                         next unless ($asset->canView && $asset->canAdd($self->session) && $asset->getUiLevel <= $self->session->user->profileField("uiLevel"));
572                         my $url = $self->getUrl("func=add;class=".$class.";prototype=".$id);
573                         $url = $self->session->url->append($url,$addToUrl) if ($addToUrl);
574                         $links{$asset->getTitle}{url} = $url;
575                         $links{$asset->getTitle}{icon} = $asset->getIcon;
576                         $links{$asset->getTitle}{'icon.small'} = $asset->getIcon(1);
577                         $links{$asset->getTitle}{'isPrototype'} = 1;
578                         $links{$asset->getTitle}{'asset'} = $asset;
579                 }
580                 $sth->finish;
581         }
582         my @sortedLinks;
583         foreach my $label (sort keys %links) {
584                 push(@sortedLinks,{
585                         label=>$label,
586                         url=>$links{$label}{url},
587                         icon=>$links{$label}{icon},
588                         'icon.small'=>$links{$label}{'icon.small'},
589                         isPrototype=>$links{$label}{isPrototype},
590                         asset=>$links{$label}{asset}
591                         });     
592         }
593         return \@sortedLinks;
594 }
595
596
597 #-------------------------------------------------------------------
598
599 =head2 getContainer ( )
600
601 Returns a reference to the container asset. If this asset is a container it returns a reference to itself. If this asset is not attached to a container it returns its parent.
602
603 =cut
604
605 sub getContainer {
606         my $self = shift;
607         if (WebGUI::Utility::isIn($self->get("className"), @{$self->session->config->get("assetContainers")})) {
608                 return $self;
609         } else {
610 #               $self->session->asset($self->getParent);
611                 return $self->getParent;
612         }
613 }
614
615 #-------------------------------------------------------------------
616
617 =head2 getDefault ( session )
618
619 Constructor. Returns the default object, which is also known by some as the "Home Page". The default object is set in the settings.
620
621 =head3 session
622
623 A reference to the current session.
624
625 =cut
626
627 sub getDefault {
628         my $class = shift;
629         my $session = shift;
630         return $class->newByDynamicClass($session, $session->setting->get("defaultPage"));
631 }
632
633 #-------------------------------------------------------------------
634
635 =head2 getEditTabs ()
636
637 Returns a list of arrayrefs, one per extra tab to add to the edit
638 form.  The default is no extra tabs.  Override this in a subclass to
639 add extra tabs.
640
641 =cut
642
643 sub getEditTabs {
644         my $self = shift;
645         return ();
646 }
647
648 #-------------------------------------------------------------------
649
650 =head2 getEditForm ()
651
652 Creates and returns a tabform to edit parameters of an Asset.
653
654 =cut
655
656 sub getEditForm {
657         my $self = shift;
658         my $i18n = WebGUI::International->new($self->session, "Asset");
659         my $ago = $i18n->get("ago");
660         my $rs = $self->session->db->read("select revisionDate from assetData where assetId=? order by revisionDate desc limit 5", [$self->getId]);
661         my $uiLevelOverride = $self->get("className");
662         $uiLevelOverride =~ s/\:\:/_/g;
663         my $tabform = WebGUI::TabForm->new($self->session,undef,undef,$self->getUrl(),$uiLevelOverride);
664         if ($self->session->config->get("enableSaveAndCommit")) {
665                 $tabform->submitAppend(WebGUI::Form::submit($self->session, {name=>"saveAndCommit", value=>$i18n->get("save and commit")}));
666         }
667         $tabform->hidden({
668                 name=>"func",
669                 value=>"editSave"
670                 });
671         if ($self->getId eq "new") {
672                 $tabform->hidden({
673                         name=>"assetId",
674                         value=>"new"
675                         });
676                 $tabform->hidden({
677                         name=>"class",
678                         value=>$self->session->form->process("class","className")
679                         });
680         } else {
681                 my $ac = $self->getAdminConsole;
682                 $ac->addSubmenuItem($self->getUrl("func=manageRevisions"),$i18n->get("revisions").":");
683                 while (my ($version) = $rs->array) {
684                         my ($interval, $units) = $self->session->datetime->secondsToInterval(time() - $version);
685                         $ac->addSubmenuItem($self->getUrl("func=edit;revision=".$version), $interval." ".$units." ".$ago);
686                 }
687         }
688         if ($self->session->form->process("proceed")) {
689                 $tabform->hidden({
690                         name=>"proceed",
691                         value=>$self->session->form->process("proceed")
692                         });
693         }
694         # create tabs
695         $tabform->addTab("properties",$i18n->get("properties"));
696         $tabform->addTab("display",$i18n->get(105),5);
697         $tabform->addTab("security",$i18n->get(107),6);
698         $tabform->addTab("meta",$i18n->get("Metadata"),3);
699         # process errors
700         my $errors = $self->session->stow->get('editFormErrors');
701         if ($errors) {
702                 $tabform->getTab("properties")->readOnly(
703                         -value=>"<p>Some error(s) occurred:<ul><li>".join('</li><li>', @$errors).'</li></ul></p>',
704                 )
705         }
706         $tabform->getTab("properties")->readOnly(
707                 -label=>$i18n->get("asset id"),
708                 -value=>$self->get("assetId"),
709                 -hoverHelp=>$i18n->get('asset id description'),
710                 );
711
712         foreach my $tabspec ($self->getEditTabs) {
713                 $tabform->addTab(@$tabspec);
714         }
715
716         foreach my $definition (reverse @{$self->definition($self->session)}) {
717                 my $properties = $definition->{properties};
718                 next unless ($definition->{autoGenerateForms});
719
720                 foreach my $fieldName (keys %{$properties}) {
721                         my %fieldHash = %{$properties->{$fieldName}};
722                         my %params = (name => $fieldName,
723                                       value => $self->getValue($fieldName));
724                         next if exists $fieldHash{autoGenerate} and not $fieldHash{autoGenerate};
725
726                         # Kludge.
727                         if (isIn($fieldHash{fieldType}, 'selectBox', 'workflow') and ref $params{value} ne 'ARRAY') {
728                                 $params{value} = [$params{value}];
729                         }
730
731                         if (exists $fieldHash{visible} and not $fieldHash{visible}) {
732                                 $params{fieldType} = 'hidden';
733                         } else {
734                                 %params = (%params, %fieldHash);
735                                 delete $params{tab};
736                         }
737
738                         my $tab = $fieldHash{tab} || "properties";
739                         $tabform->getTab($tab)->dynamicField(%params);
740                 }
741         }
742
743         if ($self->session->setting->get("metaDataEnabled")) {
744                 my $meta = $self->getMetaDataFields();
745                 foreach my $field (keys %$meta) {
746                         my $fieldType = $meta->{$field}{fieldType} || "text";
747                         my $options;
748                         # Add a "Select..." option on top of a select list to prevent from
749                         # saving the value on top of the list when no choice is made.
750                         if($fieldType eq "selectList") {
751                                 $options = {"", $i18n->get("Select")};
752                         }
753                         $tabform->getTab("meta")->dynamicField(
754                                                 name=>"metadata_".$meta->{$field}{fieldId},
755                                                 label=>$meta->{$field}{fieldName},
756                                                 uiLevel=>5,
757                                                 value=>$meta->{$field}{value},
758         &n