diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 5723ff242..6b4faeb2a 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -66,6 +66,7 @@ flexibility (multiple groups to approve, do on approve) - fixed: CS mail retrieval doesn't decode subject properly - fixed: email password recovery fields effected by UI level + - Complete rewrite of the Asset Manager 7.5.10 - fix: Syntax error in GetCsMail diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm index 70fd53ecd..dfcfeac45 100644 --- a/lib/WebGUI/Asset.pm +++ b/lib/WebGUI/Asset.pm @@ -1256,7 +1256,7 @@ sub getToolbar { $output .= 'contextMenu.addLink("'.$self->getUrl("func=demote").'","'.$i18n->get("demote").'");'; } if ($userUiLevel >= $uiLevels->{"manage"}) { - $output .= 'contextMenu.addLink("'.$self->getUrl("func=manageAssets").'","'.$i18n->get("manage").'");'; + $output .= 'contextMenu.addLink("'.$self->getUrl("op=manageAssets").'","'.$i18n->get("manage").'");'; } $output .= 'contextMenu.print(); //]]> @@ -1409,302 +1409,6 @@ sub logView { return undef; } - -#------------------------------------------------------------------- - -=head2 manageAssets ( ) - -Main page to manage assets. Renders an AdminConsole with a list of assets. If canEdit returns False, renders an insufficient privilege page. Is called by www_manageAssets - -=cut - -sub manageAssets { - my $self = shift; - my $i18n = WebGUI::International->new($self->session, "Asset"); - my $userUILevel = $self->session->user->profileField("uiLevel"); - my $uiLevels = $self->session->config->get("assetToolbarUiLevel"); - my $ancestors = $self->getLineage(["self","ancestors"],{returnObjects=>1}); - my @crumbtrail; - foreach my $ancestor (@{$ancestors}) { - if ($ancestor->getId eq $self->getId) { - my $title = $self->getTitle; - $title =~ s/\'/\\\'/g; - my $more = '\n"; - push(@crumbtrail,$more); - } else { - push(@crumbtrail,''.$ancestor->getTitle.''); - } - } - my $output = '
'.join(" > ",@crumbtrail).'
'; - $output .= " -
-   -
-
'.$i18n->get(1083).''; - $self->session->output->print($output,1); - $output = ''; - foreach my $link (@{$self->getAssetAdderLinks("proceed=manageAssets","assetContainers")}) { - $output .= '

'.$link->{label}.'

- '.$link->{label}.' '; - $output .= $self->session->icon->edit("func=edit;proceed=manageAssets",$link->{asset}->get("url")) if ($link->{isPrototype}); - $output .= '
'; - } - $output .= '
'; - foreach my $link (@{$self->getAssetAdderLinks("proceed=manageAssets")}) { - $output .= '

'.$link->{label}.'

- '.$link->{label}.' '; - $output .= $self->session->icon->edit("func=edit;proceed=manageAssets",$link->{asset}->get("url")) if ($link->{isPrototype}); - $output .= '
'; - } - $output .= '
'; - foreach my $link (@{$self->getAssetAdderLinks("proceed=manageAssets","utilityAssets")}) { - $output .= '

'.$link->{label}.'

- '.$link->{label}.' '; - $output .= $self->session->icon->edit("func=edit;proceed=manageAssets",$link->{asset}->get("url")) if ($link->{isPrototype}); - $output .= '
'; - } - $output .= '
'; - $self->session->output->print($output); - $output = ''; - my %options; - tie %options, 'Tie::IxHash'; - my $hasClips = 0; - foreach my $asset (@{$self->getAssetsInClipboard(1)}) { - $options{$asset->getId} = ''.$asset->getName.' '.$asset->getTitle; - $hasClips = 1; - } - if ($hasClips) { - $output .= '
'.$i18n->get(1082).'' - .WebGUI::Form::formHeader($self->session, {action=>$self->getUrl}) - .WebGUI::Form::hidden($self->session,{name=>"func",value=>"pasteList"}) - .WebGUI::Form::checkbox($self->session,{extras=>'onclick="toggleClipboardSelectAll(this.form);"'}) - .' '.$i18n->get("select all").'
' - .WebGUI::Form::checkList($self->session,{name=>"assetId",vertical=>1,options=>\%options}) - .'
' - .WebGUI::Form::submit($self->session,{value=>"Paste"}) - .WebGUI::Form::formFooter($self->session) - .'
' - .''; - } - $self->session->output->print($output); - $output = '
'.$i18n->get("packages").''; - foreach my $asset (@{$self->getPackageList}) { - $output .= '

'.$asset->getName.'

- getId).'">'.$asset->getTitle.' ' - .$self->session->icon->edit("func=edit;proceed=manageAssets",$asset->get("url")) - .$self->session->icon->export("func=exportPackage",$asset->get("url")) - .'
'; - } - $output .= '
'.WebGUI::Form::formHeader($self->session, {action=>$self->getUrl}) - .WebGUI::Form::hidden($self->session, {name=>"func", value=>"importPackage"}) - .'' - .WebGUI::Form::submit($self->session, {value=>$i18n->get("import"), extras=>'style="font-size: 10px;"'}) - .WebGUI::Form::formFooter($self->session); - $output .= '
-
-   -
- '; - $self->session->output->print($output); - return undef; -} - -#------------------------------------------------------------------- - -=head2 manageAssetsSearch ( ) - -Returns the interface for searching within the asset manager. - -=cut - -sub manageAssetsSearch { - my $self = shift; - my $i18n = WebGUI::International->new($self->session, "Asset"); - my $output = WebGUI::Form::formHeader($self->session); - $output .= WebGUI::Form::text($self->session, { name=>"keywords", value=>$self->session->form->get("keywords")}); - my %classes = (); - tie %classes, "Tie::IxHash"; - %classes = ("any"=>"Any Class", $self->session->db->buildHash("select distinct(className) from asset")); - delete $classes{"WebGUI::Asset"}; # don't want to search for the root asset - $output .= WebGUI::Form::selectBox($self->session, {name=>"class", value=>$self->session->form->process("class","className"), defaultValue=>"any", options=>\%classes}); - $output .= WebGUI::Form::hidden($self->session, {name=>"func", value=>"manageAssets"}); - $output .= WebGUI::Form::hidden($self->session, {name=>"doit", value=>"1"}); - $output .= WebGUI::Form::submit($self->session, {value=>"Search"}); - $output .= WebGUI::Form::formFooter($self->session); - $self->session->output->print($output); - $output = ''; - return undef unless ($self->session->form->get("doit") && ($self->session->form->get("keywords") ne "" || $self->session->form->get("class") ne "any")); - my $class = $self->session->form->process("class","className") eq "any" ? undef : $self->session->form->process("class","className"); - my $assets = WebGUI::Search->new($self->session,0)->search({ - keywords=>$self->session->form->get("keywords"), - classes=>[$class] - })->getAssets; - $output .= "
 
'; - $self->session->output->print($output); - return undef; -} - #------------------------------------------------------------------- =head2 new ( session, assetId [, className, revisionDate ] ) @@ -2453,7 +2157,7 @@ sub www_changeUrlConfirm { } if ($self->session->form->param("proceed") eq "manageAssets") { - $self->session->http->setRedirect($self->getUrl('func=manageAssets')); + $self->session->http->setRedirect($self->getUrl('op=manageAssets')); } else { $self->session->http->setRedirect($self->getUrl()); } @@ -2581,35 +2285,15 @@ sub www_editSave { =head2 www_manageAssets ( ) -Main page to manage/search assets. Renders an AdminConsole with a list of assets. If canEdit returns False, renders an insufficient privilege page. Is called by www_manageAssets +Redirect to the asset manager content handler (for backwards +compatibility) =cut sub www_manageAssets { - my $self = shift; - return $self->session->privilege->insufficient() unless $self->canEdit; - $self->session->style->setLink($self->session->url->extras('contextMenu/contextMenu.css'), {rel=>"stylesheet",type=>"text/css"}); - $self->session->style->setScript($self->session->url->extras('contextMenu/contextMenu.js'), {type=>"text/javascript"}); - $self->session->style->setLink($self->session->url->extras('assetManager/assetManager.css'), {rel=>"stylesheet",type=>"text/css"}); - $self->session->style->setScript($self->session->url->extras('assetManager/assetManager.js'), {type=>"text/javascript"}); - if ($self->session->form->get("search")) { - $self->session->scratch->set("manageAssetsSearchToggle",1); - } elsif ($self->session->form->get("manage")) { - $self->session->scratch->delete("manageAssetsSearchToggle"); - } - my $out = $self->getAdminConsole->render("~~~"); - my ($head, $foot) = split("~~~",$out); - $self->session->style->sent(1); - $self->session->http->sendHeader; - $self->session->output->print($head); - $self->session->output->print('
Manage | Search
',1); - if ($self->session->scratch->get("manageAssetsSearchToggle")) { - $self->manageAssetsSearch; - } else { - $self->manageAssets; - } - $self->session->output->print($foot); - return "chunked"; + my $self = shift; + $self->session->http->setRedirect( $self->getUrl( 'op=assetManager' ) ); + return "redirect"; } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm b/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm index a4b462c84..f962fb0fa 100644 --- a/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm +++ b/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm @@ -950,13 +950,13 @@ sub www_edit { # Raw HTML here to provide proper value for the image $file->{ form_promote } - = qq{} ; $file->{ form_demote } - = qq{} ; diff --git a/lib/WebGUI/Asset/Wobject/Navigation.pm b/lib/WebGUI/Asset/Wobject/Navigation.pm index 54dbaace0..ae5b65065 100644 --- a/lib/WebGUI/Asset/Wobject/Navigation.pm +++ b/lib/WebGUI/Asset/Wobject/Navigation.pm @@ -312,7 +312,7 @@ sub getToolbar { return ''.$toolbar; diff --git a/lib/WebGUI/AssetClipboard.pm b/lib/WebGUI/AssetClipboard.pm index 6ec29e1ef..90a1b1bd2 100644 --- a/lib/WebGUI/AssetClipboard.pm +++ b/lib/WebGUI/AssetClipboard.pm @@ -414,7 +414,7 @@ $self->session->style->setLink($self->session->url->extras('assetManager/assetMa name=>'assetId', value=>$child->getId }) - ."','" . $plus . "getUrl("func=manageAssets")."\">" . $title + ."','" . $plus . "getUrl("op=manageAssets")."\">" . $title ."','

getIcon(1)."\" style=\"border-style:none;vertical-align:middle;\" alt=\"".$child->getName."\" />

".$child->getName ."','".$self->session->datetime->epochToHuman($child->get("revisionDate")) ."','".formatBytes($child->get("assetSize"))."');\n"; diff --git a/lib/WebGUI/AssetTrash.pm b/lib/WebGUI/AssetTrash.pm index 60fb528e0..5a8027481 100644 --- a/lib/WebGUI/AssetTrash.pm +++ b/lib/WebGUI/AssetTrash.pm @@ -339,7 +339,7 @@ sub www_manageTrash { name=>'assetId', value=>$child->getId }) - ."','" . $plus . "getUrl("func=manageAssets")."\">" . $title + ."','" . $plus . "getUrl("op=manageAssets")."\">" . $title ."','

getIcon(1)."\" style=\"vertical-align:middle;border-style:none;\" alt=\"".$child->getName."\" />

".$child->getName ."','".$self->session->datetime->epochToHuman($child->get("revisionDate")) ."','".formatBytes($child->get("assetSize"))."');\n"; diff --git a/lib/WebGUI/Content/AssetManager.pm b/lib/WebGUI/Content/AssetManager.pm new file mode 100644 index 000000000..dd0049e5b --- /dev/null +++ b/lib/WebGUI/Content/AssetManager.pm @@ -0,0 +1,812 @@ +package WebGUI::Content::AssetManager; + +use strict; + +use URI; +use WebGUI::Form; +use WebGUI::Paginator; +use WebGUI::Utility; + +#---------------------------------------------------------------------------- + +=head2 getClassSelectBox ( session ) + +Gets a select box to choose a class name. + +=cut + +sub getClassSelectBox { + my $session = shift; + + tie my %classes, "Tie::IxHash", ( + "" => "Any Class", + $session->db->buildHash("select distinct(className) from asset"), + ); + delete $classes{"WebGUI::Asset"}; # don't want to search for the root asset + + return WebGUI::Form::selectBox( $session, { + name => "class", + value => $session->form->process("class","className"), + defaultValue => "", + options => \%classes, + }); +} + +#---------------------------------------------------------------------------- + +=head2 getCurrentAsset ( session ) + +Returns the asset we would be looking at if we weren't looking at the Asset +Manager. + +=cut + +sub getCurrentAsset { + my $session = shift; + return WebGUI::Asset->newByUrl( $session ); +} + +#---------------------------------------------------------------------------- + +=head2 getHeader ( session ) + +Get a header to pick "Manage" or "Search". Add other things later maybe? + +=cut + +sub getHeader { + my $session = shift; + my $output = ''; + my $i18n = WebGUI::International->new( $session, "Asset" ); + + if ( $session->form->get( 'method' ) eq "search" ) { + $output .= '
' + . join( " | ", + q{} . $i18n->get( 'manage' ) . q{}, + q{} . $i18n->get( "search" ) . q{}, + ) + . q{
} + ; + } + else { + $output .= '
' + . join( " | ", + q{} . $i18n->get( "manage" ) . q{}, + q{} . $i18n->get( "search" ) . q{}, + ) + . q{
} + ; + } + + return $output; +} + +#---------------------------------------------------------------------------- + +=head2 getManagerPaginator ( session ) + +Get a page for the Asset Manager view. Returns a WebGUI::Paginator object +filled with asset IDs. + +=cut + +sub getManagerPaginator { + my $session = shift; + my $asset = getCurrentAsset( $session ); + + my $orderByColumn = $session->form->get( 'orderByColumn' ) + || "lineage" + ; + my $orderByDirection = $session->form->get( 'orderByDirection' ) eq "DESC" + ? "DESC" + : "ASC" + ; + + my $p + = WebGUI::Paginator->new( $session, + '?op=assetManager;method=manage;orderByColumn=' . $orderByColumn + . ';orderByDirection=' . $orderByDirection + ); + + my $orderBy = $session->db->dbh->quote_identifier( $orderByColumn ) . ' ' . $orderByDirection; + $p->setDataByArrayRef( $asset->getLineage( ['children'] ), { orderByClause => $orderBy } ); + + return $p; +} + +#---------------------------------------------------------------------------- + +=head2 getSearchPaginator ( session, query ) + +Get a page for the Asset Search view. Returns a WebGUI::Paginator object +filled with asset IDs. + +=cut + +sub getSearchPaginator { + my $session = shift; + my $query = shift; + my %parms; + + my $s = WebGUI::Search->new( $session, 0 ); + $s->search( { + keywords => $query->{ keywords }, + classes => $query->{ classes }, + } ); + + my $queryString = 'op=assetManager;method=search;keywords=' . $query->{ keywords }; + for my $class ( @{ $query->{ classes } } ) { + $queryString .= ';class=' . $class; + } + + my $p = $s->getPaginatorResultSet( $session->url->page( $queryString ) ); + + return $p; +} + +#---------------------------------------------------------------------------- + +=head2 getMoreMenu ( asset, label ) + +Gets the "More" menu with the specified label. + +=cut + +sub getMoreMenu { + my $asset = shift; + my $label = shift || "More"; + my $userUiLevel = $asset->session->user->profileField("uiLevel"); + my $toolbarUiLevel = $asset->session->config->get("assetToolbarUiLevel"); + my $i18n = WebGUI::International->new( $asset->session, "Asset" ); + + ### The More menu + my @more_fields = (); # The fields to fill in the more menu + my $more_markup = q{} . $label . q{} + . q{} + . q{} + ; + + return sprintf $more_markup, @more_fields; +} + +#---------------------------------------------------------------------------- + +=head2 getOrderLink ( session, column, label ) + +Gets a link to order the results based on a column. Uses some magick +to ensure proper working no matter where we are. + +=cut + +sub getOrderLink { + my $session = shift; + my $column = shift; + my $label = shift; + + my $columnParam = "orderByColumn"; + my $directionParam = "orderByDirection"; + + my $url = URI->new( $session->env->get( "REQUEST_URI" ) ); + my $query = $url->query; + # Split query string into param => value hash + my %query = map { /(.+)=(.+)/; $1 => $2 } split /[;&]/, $query; + + # Delete unnecessary keys + delete $query{ 'assetId' }; + + # Add necessary keys + $query{ $columnParam } = $column; + + if ( $session->form->get( $columnParam ) eq $column && $session->form->get( $directionParam ) eq "ASC" ) { + $query{ $directionParam } = "DESC"; + } + else { + $query{ $directionParam } = "ASC"; + } + + $url->query_form( %query ); + + return q{} + . $label + . q{} + ; +} + +#---------------------------------------------------------------------------- + +=head2 handler ( session ) + +Handle the session, if we can. Otherwise pass it on. + +Check permissions + +=cut + +# BAD things about procedural that would be fixed with class methods +# 1) I must use stringy eval to call my method, instead of $class->$method( args ) +# 2) I must validate that method using a list of known methods instead of $class->can( $method ) + +sub handler { + my ( $session ) = @_; + + if ( $session->form->get( 'op' ) eq 'assetManager' && getCurrentAsset( $session ) ) { + + return $session->privilege->noAccess unless getCurrentAsset( $session )->canEdit; + + my $method = $session->form->get( 'method' ) + ? 'www_' . $session->form->get( 'method' ) + : 'www_manage' + ; + + # Validate the method name + if ( !grep { $_ eq $method } qw( www_manage www_search ) ) { + return "Invalid method"; + } + else { + # I hate hate hate stringy eval + return eval "$method" . '($session)'; + } + } + else { + return; + } +} + +#---------------------------------------------------------------------------- + +=head2 www_manage ( session ) + +Show the main screen of the asset manager, paginated. Also load the +JavaScript that will take over if the browser has the cojones. + +# BAD things about procedural that would be fixed with class methods +# 3) The "appendSideLinks" method would be better as simply "getAdminStyle" and would do more + +=cut + +sub www_manage { + my ( $session ) = @_; + my $ac = WebGUI::AdminConsole->new( $session, "assets" ); + my $currentAsset = getCurrentAsset( $session ); + my $i18n = WebGUI::International->new( $session, "Asset" ); + + ### Do Action + if ( my $action = $session->form->get( 'action' ) ) { + my @assetIds = $session->form->get( 'assetId' ); + + if ( $action eq "update" ) { + for my $assetId ( @assetIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + next unless $asset; + my $rank = $session->form->get( $assetId . '_rank' ); + next unless $rank; # There's no such thing as zero + + $asset->setRank( $rank ); + } + } + elsif ( $action eq "trash" ) { + for my $assetId ( @assetIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + next unless $asset; + $asset->trash; + } + } + elsif ( $action eq "cut" ) { + for my $assetId ( @assetIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + next unless $asset; + $asset->cut; + } + } + elsif ( $action eq "copy" ) { + for my $assetId ( @assetIds ) { + # Copy == Duplicate + Cut + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId); + my $newAsset = $asset->duplicate( { skipAutoCommitWorkflows => 1 } ); + $newAsset->update( { title => $newAsset->getTitle . ' (copy)' } ); + $newAsset->cut; + } + } + elsif ( $action eq "duplicate" ) { + for my $assetId ( @assetIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + next unless $asset; + $asset->duplicate( { skipAutoCommitWorkflows => 1 } ); + } + } + } + + # Show the page + $session->style->setLink( $session->url->extras( 'yui-webgui/build/assetManager/assetManager.css' ), { rel => "stylesheet", type => 'text/css' } ); + $session->style->setScript( $session->url->extras( 'yui/build/yahoo-dom-event/yahoo-dom-event.js' ) ); + $session->style->setScript( $session->url->extras( 'yui-webgui/build/assetManager/assetManager.js' ) ); + $session->style->setScript( $session->url->extras( 'yui-webgui/build/form/form.js' ) ); + $session->style->setRawHeadTags( <<'ENDHTML' ); + +ENDHTML + my $output = '
' . getHeader( $session ); + + ### Crumbtrail + my $crumb_markup = '
  • > %s
  • '; + my $ancestors = $currentAsset->getLineage( ['ancestors'], { returnObjects => 1 } ); + + $output .= '
      '; + for my $asset ( @{ $ancestors } ) { + $output .= sprintf $crumb_markup, + $asset->getUrl( 'op=assetManager;method=manage' ), + $asset->get( "menuTitle" ), + ; + } + + # And ourself + $output .= sprintf '
    1. > %s
    2. ', + getMoreMenu( $currentAsset, $currentAsset->get( "menuTitle" ) ), + ; + $output .= '
    '; + + ### The page of assets + $output .= q{
    } + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} # Checkbox column + . q{} # Rank column + . q{} # Edit / More + . q{} # Title + . q{} # Type + . q{} # Revision Date + . q{} # Size + . q{} # Lock + . q{} + . q{} + ; + + # The markup for a single asset + my $row_markup = q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + ; + + # The field keys to fill in the placeholders + my @row_fields = qw( + alt + assetId + assetId rank + editLink moreMenu + hasChildren url title + iconUrl type + revisionDate + size + url lockIcon + ); + + my $p = getManagerPaginator( $session, { + orderByColumn => $session->form->get( 'orderByColumn' ), + orderByDirection => $session->form->get( 'orderByDirection' ), + } ); + my $count = 0; + for my $assetId ( @{ $p->getPageData } ) { + $count++; + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + + # Populate the required fields to fill in + my %fields = ( + alt => ( $count % 3 == 0 ? 'class="alt"' : '' ), + assetId => $asset->getId, + url => $asset->getUrl, + title => $asset->get( "title" ), + revisionDate => $session->datetime->epochToHuman( $asset->get( "revisionDate" ) ), + hasChildren => ( $asset->hasChildren ? "+ " : "  " ), + rank => $asset->getRank, + size => formatBytes( $asset->get( 'assetSize' ) ), + ); + + # The asset icon + my $icon = [ grep { $_->{ icon } } @{ $asset->definition( $session ) } ]->[ 0 ]->{ icon }; + $fields{ iconUrl } = $session->url->extras( '/assets/small/' . $icon ); + + # The asset type (i18n name) + my $type = [ grep { $_->{ assetName } } @{ $asset->definition( $session ) } ]->[ 0 ]->{ assetName }; + $fields{ type } = $type; + + # The lock + if ( $asset->lockedBy ) { # lockedBy in case someone overrides isLocked (like the Collab System Thread ) + $fields{ lockIcon } + = sprintf 'locked by %s', + $session->url->extras( 'assetManager/locked.gif' ), + WebGUI::HTML::format( $asset->lockedBy->username, "text" ), + WebGUI::HTML::format( $asset->lockedBy->username, "text" ), + ; + } + else { + $fields{ lockIcon } + = sprintf 'unlocked', + $session->url->extras( 'assetManager/unlocked.gif' ), + ; + } + + # The edit link + if ( !$asset->lockedBy || $asset->canEditIfLocked ) { + $fields{ editLink } + = sprintf '' . $i18n->get( "edit" ) . ' |', + $asset->getUrl( 'func=edit;proceed=manageAssets' ) + ; + } + + # The More menu + $fields{ moreMenu } = getMoreMenu( $asset, $i18n->get( 'menu label' ) ); + + $output .= sprintf $row_markup, @fields{ @row_fields }; + } + + $output .= q{} + . q{
    } . getOrderLink( $session, "lineage", $i18n->get( "rank" ) ) . q{ } . getOrderLink( $session, "title", $i18n->get( "99" ) ) . q{} . getOrderLink( $session, "className", $i18n->get( "type" ) ) . q{} . getOrderLink( $session, "revisionDate", $i18n->get( "last updated" ) ) . q{} . getOrderLink( $session, "assetSize", $i18n->get( "size" ) ) . q{} . getOrderLink( $session, "lockedBy", $i18n->get( "locked" ) ) . q{
    %s %s%s%s %s%s%s%s
    } + . q{

    } . $i18n->get( 'with selected' ) + . q{} + . q{} + . q{} + . q{} + . q{} + . q{

    } + . q{
    } + ; + + ### Page links + $output .= q{}; + + ### Page description + $output .= sprintf q{
    } . $i18n->get( 'page indicator' ) . q{
    }, + $p->getPageNumber, + $p->getNumberOfPages, + ; + + ### Clearing div + $output .= q{
     
    }; + + ### New Content + $output .= q{
    } . $i18n->get( '1083' ) . ''; + foreach my $link (@{$currentAsset->getAssetAdderLinks("proceed=manageAssets","assetContainers")}) { + $output .= '

    '.$link->{label}.'

    + '.$link->{label}.' '; + $output .= $session->icon->edit("func=edit;proceed=manageAssets",$link->{asset}->get("url")) if ($link->{isPrototype}); + $output .= '
    '; + } + $output .= '
    '; + foreach my $link (@{$currentAsset->getAssetAdderLinks("proceed=manageAssets")}) { + $output .= '

    '.$link->{label}.'

    + '.$link->{label}.' '; + $output .= $session->icon->edit("func=edit;proceed=manageAssets",$link->{asset}->get("url")) if ($link->{isPrototype}); + $output .= '
    '; + } + $output .= '
    '; + foreach my $link (@{$currentAsset->getAssetAdderLinks("proceed=manageAssets","utilityAssets")}) { + $output .= '

    '.$link->{label}.'

    + '.$link->{label}.' '; + $output .= $session->icon->edit("func=edit;proceed=manageAssets",$link->{asset}->get("url")) if ($link->{isPrototype}); + $output .= '
    '; + } + $output .= '
    '; + + tie my %options, 'Tie::IxHash'; + my $hasClips = 0; + foreach my $asset (@{$currentAsset->getAssetsInClipboard(1)}) { + $options{$asset->getId} = ''.$asset->getName.' '.$asset->getTitle; + $hasClips = 1; + } + if ($hasClips) { + $output .= '
    '.$i18n->get(1082).'' + .WebGUI::Form::formHeader($session, {action=>$currentAsset->getUrl}) + .WebGUI::Form::hidden($session,{name=>"func",value=>"pasteList"}) + .WebGUI::Form::checkbox($session,{extras=>'onclick="toggleClipboardSelectAll(this.form);"'}) + .' '.$i18n->get("select all").'
    ' + .WebGUI::Form::checkList($session,{name=>"assetId",vertical=>1,options=>\%options}) + .'
    ' + .WebGUI::Form::submit($session,{value=>"Paste"}) + .WebGUI::Form::formFooter($session) + .'
    ' + .''; + } + + ## Packages + $output .= '
    '.$i18n->get("packages").''; + foreach my $asset (@{$currentAsset->getPackageList}) { + $output .= '

    '.$asset->getName.'

    + getId).'">'.$asset->getTitle.' ' + .$session->icon->edit("func=edit;proceed=manageAssets",$asset->get("url")) + .$session->icon->export("func=exportPackage",$asset->get("url")) + .'
    '; + } + $output .= '
    '.WebGUI::Form::formHeader($session, {action=>$currentAsset->getUrl}) + .WebGUI::Form::hidden($session, {name=>"func", value=>"importPackage"}) + .'' + .WebGUI::Form::submit($session, {value=>$i18n->get("import"), extras=>'style="font-size: 10px;"'}) + .WebGUI::Form::formFooter($session); + $output .= '
    '; + + ### Clearing div + $output .= q{
     
    }; + $output .= q{
    }; + + return $ac->render( $output ); +} + +#---------------------------------------------------------------------------- + +=head2 www_search ( session ) + +Search assets underneath this asset. + +=cut + +sub www_search { + my $session = shift; + my $ac = WebGUI::AdminConsole->new( $session, "assets" ); + my $i18n = WebGUI::International->new( $session, "Asset" ); + my $output = '
    ' . getHeader( $session ); + + $session->style->setLink( $session->url->extras( 'yui-webgui/build/assetManager/assetManager.css' ), { rel => "stylesheet", type => 'text/css' } ); + $session->style->setScript( $session->url->extras( 'yui/build/yahoo-dom-event/yahoo-dom-event.js' ) ); + $session->style->setScript( $session->url->extras( 'yui-webgui/build/assetManager/assetManager.js' ) ); + $session->style->setScript( $session->url->extras( 'yui-webgui/build/form/form.js' ) ); + $session->style->setRawHeadTags( <<'ENDHTML' ); + +ENDHTML + + ### Show the form + $output .= q{

    } + . q{} + . q{} + . q{} + . getClassSelectBox( $session ) + . q{} + . q{

    } + ; + + ### Actions + if ( my $action = $session->form->get( 'action' ) ) { + my @assetIds = $session->form->get( 'assetId' ); + + if ( $action eq "trash" ) { + for my $assetId ( @assetIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + next unless $asset; + $asset->trash; + } + } + elsif ( $action eq "cut" ) { + for my $assetId ( @assetIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + next unless $asset; + $asset->cut; + } + } + elsif ( $action eq "copy" ) { + for my $assetId ( @assetIds ) { + # Copy == Duplicate + Cut + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId); + my $newAsset = $asset->duplicate( { skipAutoCommitWorkflows => 1 } ); + $newAsset->update( { title => $newAsset->getTitle . ' (copy)' } ); + $newAsset->cut; + } + } + } + + ### Run the search + if ( $session->form->get( 'keywords' ) || $session->form->get( 'class' ) ) { + my $keywords = $session->form->get( 'keywords' ); + my @classes = $session->form->get( 'class' ); + + my $p = getSearchPaginator( $session, { + keywords => $keywords, + classes => \@classes, + orderByColumn => $session->form->get( 'orderByColumn' ), + orderByDirection => $session->form->get( 'orderByDirection' ), + } ); + + if ( $p->getRowCount == 0 ) { + $output .= q{

    } . $i18n->get( 'no results' ) . q{

    }; + } + else { + ### Display the search results + $output .= q{
    } + . q{} + . q{} + . q{} + . q{} + ; + + # Add classes to the form + for my $class ( @classes ) { + $output .= q{}; + } + + $output .= q{} + . q{} + . q{} + . q{} # Checkbox column + . q{} # Edit / More + . q{} # Title + . q{} # Type + . q{} # Revision Date + . q{} # Size + . q{} # Lock + . q{} + . q{} + ; + + # The markup for a single asset + my $row_markup = q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + . q{} + ; + + # The field keys to fill in the placeholders + my @row_fields = qw( + alt + assetId + editLink moreMenu + title + iconUrl type + revisionDate + size + url lockIcon + ); + + my $count = 0; + for my $assetInfo ( @{ $p->getPageData } ) { + $count++; + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetInfo->{ assetId } ); + + # Populate the required fields to fill in + my %fields = ( + alt => ( $count % 3 == 0 ? 'class="alt"' : '' ), + assetId => $asset->getId, + url => $asset->getUrl, + title => $asset->get( "title" ), + revisionDate => $session->datetime->epochToHuman( $asset->get( "revisionDate" ) ), + hasChildren => ( $asset->hasChildren ? "+ " : "  " ), + rank => $asset->getRank, + size => formatBytes( $asset->get( 'assetSize' ) ), + ); + + # The asset icon + my $icon = [ grep { $_->{ icon } } @{ $asset->definition( $session ) } ]->[ 0 ]->{ icon }; + $fields{ iconUrl } = $session->url->extras( '/assets/small/' . $icon ); + + # The asset type (i18n name) + my $type = [ grep { $_->{ assetName } } @{ $asset->definition( $session ) } ]->[ 0 ]->{ assetName }; + $fields{ type } = $type; + + # The lock + if ( $asset->lockedBy ) { # lockedBy in case someone overrides isLocked (like the Collab System Thread ) + $fields{ lockIcon } + = sprintf 'locked by %s', + $session->url->extras( 'assetManager/locked.gif' ), + WebGUI::HTML::format( $asset->lockedBy->username, "text" ), + WebGUI::HTML::format( $asset->lockedBy->username, "text" ), + ; + } + else { + $fields{ lockIcon } + = sprintf 'unlocked', + $session->url->extras( 'assetManager/unlocked.gif' ), + ; + } + + # The edit link + if ( !$asset->lockedBy || $asset->canEditIfLocked ) { + $fields{ editLink } + = sprintf '' . $i18n->get( "edit" ) . ' |', + $asset->getUrl( 'func=edit' ) + ; + } + + # The More menu + $fields{ moreMenu } = getMoreMenu( $asset, $i18n->get( "menu label" ) ); + + $output .= sprintf $row_markup, @fields{ @row_fields }; + } + + $output .= q{} + . q{
     } . $i18n->get( '99' ) . q{} . $i18n->get( "type" ) . q{} . $i18n->get( "last updated" ) . q{} . $i18n->get( "size" ) . q{} . $i18n->get( "locked" ) . q{
    %s %s%s %s%s%s%s
    } + . q{

    } . $i18n->get( 'with selected' ) + . q{} + . q{} + . q{} + . q{

    } + . q{
    } + ; + + ### Page links + $output .= q{}; + + ### Page description + $output .= sprintf q{
    } . $i18n->get( 'page indicator' ) . q{
    }, + $p->getPageNumber, + $p->getNumberOfPages, + ; + + ### Clearing div + $output .= q{
     
    }; + } + } + + $output .= '
    '; + + return $ac->render( $output ); +} + +1; diff --git a/lib/WebGUI/Form/Template.pm b/lib/WebGUI/Form/Template.pm index bb853d55e..ad1c59e69 100644 --- a/lib/WebGUI/Form/Template.pm +++ b/lib/WebGUI/Form/Template.pm @@ -176,7 +176,7 @@ sub toHtmlWithWrapper { $returnUrl = ";proceed=goBackToPage;returnUrl=".$self->session->url->escape($self->session->asset->getUrl); } my $buttons = $self->session->icon->edit("func=edit".$returnUrl,$template->get("url")); - $buttons .= $self->session->icon->manage("func=manageAssets",$template->getParent->get("url")); + $buttons .= $self->session->icon->manage("op=manageAssets",$template->getParent->get("url")); $self->set("subtext",$buttons . $self->get("subtext")); } return $self->SUPER::toHtmlWithWrapper; diff --git a/lib/WebGUI/Search.pm b/lib/WebGUI/Search.pm index 544596428..8cdab0d3c 100644 --- a/lib/WebGUI/Search.pm +++ b/lib/WebGUI/Search.pm @@ -398,7 +398,7 @@ sub search { if ($rules->{classes}) { my @phrases = (); foreach my $class (@{$rules->{classes}}) { - next unless defined $class; + next unless $class; push(@params, $class); push(@phrases, "className=?"); } diff --git a/lib/WebGUI/URL/Content.pm b/lib/WebGUI/URL/Content.pm index 9ae6b9c82..5566ba52c 100644 --- a/lib/WebGUI/URL/Content.pm +++ b/lib/WebGUI/URL/Content.pm @@ -53,11 +53,14 @@ sub handler { $request->push_handlers(PerlResponseHandler => sub { my $session = WebGUI::Session->open($server->dir_config('WebguiRoot'), $config->getFilename, $request, $server); foreach my $handler (@{$config->get("contentHandlers")}) { - my $output = eval { WebGUI::Pluggable::run($handler, "handler", [ $session ] ) }; + my $output = WebGUI::Pluggable::run($handler, "handler", [ $session ] ); if ( my $e = WebGUI::Error->caught ) { $session->errorHandler->error($e->package.":".$e->line." - ".$e->error); $session->errorHandler->debug($e->package.":".$e->line." - ".$e->trace); } + elsif ( $@ ) { + $session->errorHandler->error( $@ ); + } else { if ($output eq "chunked") { if ($session->errorHandler->canShowDebug()) { diff --git a/lib/WebGUI/i18n/English/Asset.pm b/lib/WebGUI/i18n/English/Asset.pm index 5e1921c68..31dce68f7 100644 --- a/lib/WebGUI/i18n/English/Asset.pm +++ b/lib/WebGUI/i18n/English/Asset.pm @@ -1048,6 +1048,42 @@ Couldn't open %-s because %-s
    lastUpdated => 0, }, + 'search' => { + message => q{Search}, + lastUpdated => 0, + context => "Label for the Search function of the asset manager", + }, + + 'with selected' => { + message => q{With Selected: }, + lastUpdated => 0, + context => q{Introduction to the action buttons.}, + }, + + + 'update' => { + message => q{Update}, + lastUpdated => 0, + context => q{Label for the update action. Currently only affects rank.}, + }, + + 'page indicator' => { + message => q{Showing page %s of %s}, + lastUpdated => 0, + context => q{Which page we're on. First field is the current page. Second field is the total number of pages}, + }, + + 'no results' => { + message => q{No Results Found!}, + lastUpdated => 0, + context => q{Message when no assets match search criteria}, + }, + + 'menu label' => { + message => q{More}, + lastUpdated => 0, + context => q{Label for the menu to show actions to perform on an asset}, + }, }; 1; diff --git a/www/extras/yui-webgui/build/assetManager/assetManager.css b/www/extras/yui-webgui/build/assetManager/assetManager.css new file mode 100644 index 000000000..75e2847ec --- /dev/null +++ b/www/extras/yui-webgui/build/assetManager/assetManager.css @@ -0,0 +1,139 @@ + +/*** Asset manager Stylesheet ***/ + +/* Override retarded admin console styles */ +li, td, a, a:link, a:hover, a:visited, a:active, #assetManager { + font-size: 10pt; + font-family: "Lucida Grande", "Lucida Sans Unicode", Tahoma, Verdana, Arial, sans-serif; +} + +a:link, a:visited, a:active { + color: blue; +} + +a:hover { + color: purple; +} + +/** Page stats **/ +#pageStats { + float: right; + text-align: right; +} + +#pageLinks { + float: right; + text-align: right; +} + +/** Crumbtrail **/ +#crumbtrail ol { + float: left; +} + +#crumbtrail li { + list-style: none; + float: left; + padding-left: 0.5em; +} + +/* fix more menu */ +#crumbtrail li li { + float: none; +} + +/** More menus **/ +.moreMenu { + position: relative; +} + +.moreMenu ul { + display: none; + position: absolute; + top: 1em; + left: 0; + width: 12em; + margin: 0; + padding: 0; + background-color: white; + border: 1px solid blue; + z-index: 1000; +} + +.moreMenu li { + list-style: none; + background-color: white; +} + +.moreMenu li a { + display: block; + padding: 0.25em; + z-index: 1100; + text-align: left; + width: 10em; +} + +/** Asset Manager table **/ +.assetManager { + clear: both; + width: 100%; +} + +.assetManager th { + border: solid #666; + border-width: 0 1px 2px 0; + background-color: #eee; +} + +.assetManager th, .assetManager td { + padding: 0 0.8em; +} + +#assetManager th, #assetManager td, .assetManager { + font-size: 0.8em; +} + +.assetManager .center, .assetManager th { + text-align: center; +} + +.assetManager .right { + text-align: right; +} + +.hasChildren { + font-family: "Courier New", monospace; +} + +.assetManager .rank { + text-align: right; +} + +.assetManager a:link, .assetManager a:visited, .assetManager a:active, .assetManager a:hover { + font-size: 8pt; +} + +.assetManager tr.alt td { + background-color: #ddd; +} + +.assetManager tr.highlight td { + background-color: #CCF; +} + +/** Actions **/ +.actions { + float: left; +} + +/** The function panes **/ +.functionPane { + width: 28%; + float: left; + padding-right: 30px; + font-size: 14px; +} + +.functionPane a { + vertical-align: bottom; +} diff --git a/www/extras/yui-webgui/build/assetManager/assetManager.js b/www/extras/yui-webgui/build/assetManager/assetManager.js new file mode 100644 index 000000000..3cabe6474 --- /dev/null +++ b/www/extras/yui-webgui/build/assetManager/assetManager.js @@ -0,0 +1,180 @@ + +/*** The WebGUI Asset Manager + * Requires: YAHOO, Dom, Event + * + */ + +if ( typeof WebGUI == "undefined" ) { + WebGUI = {}; +} +if ( typeof WebGUI.AssetManager == "undefined" ) { + WebGUI.AssetManager = {}; +} + + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.addHighlightToRow ( child ) + Highlight the row containing this element by adding to it the "highlight" + class +*/ +WebGUI.AssetManager.addHighlightToRow += function ( child ) { + var row = WebGUI.AssetManager.findRow( child ); + if ( !YAHOO.util.Dom.hasClass( row, "highlight" ) ) { + YAHOO.util.Dom.addClass( row, "highlight" ); + } +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.findRow ( child ) + Find the row that contains this child element. +*/ +WebGUI.AssetManager.findRow += function ( child ) { + var node = child; + while ( node ) { + if ( node.tagName == "TR" ) { + return node; + } + node = node.parentNode; + } +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.hideMoreMenu ( event, element ) + Hide the more menu located inside element after a short delay. +*/ +WebGUI.AssetManager.hideMoreMenu += function ( event, element ) { + var ul = element.getElementsByTagName( "ul" )[0]; + // Store the timeout in the element itself (bad, yes) + element.menuTimeout + = setTimeout( function () { ul.style.display = "none"; }, 100 ); +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.initManager ( ) + Initialize the www_manage page +*/ +WebGUI.AssetManager.initManager += function () { + + WebGUI.AssetManager.initMoreMenus(); + + // Start the data source + +}; + + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.initMoreMenus ( ) + Initialize the more menus in an asset manager page +*/ +WebGUI.AssetManager.initMoreMenus += function () { + // Initialize the more menus + var moreMenus = YAHOO.util.Dom.getElementsByClassName( "moreMenu" ); + for ( var i = 0; i < moreMenus.length; i++ ) { + var a = moreMenus[ i ].getElementsByTagName( "a" ); + for ( var h = 0; h < a.length; h++ ) { + YAHOO.util.Event.addListener( a[ h ], "mouseover", WebGUI.AssetManager.showMoreMenu, moreMenus[ i ] ); + YAHOO.util.Event.addListener( a[ h ], "mouseout", WebGUI.AssetManager.hideMoreMenu, moreMenus[ i ] ); + } + } +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.initSearch ( ) + Initialize the www_search page +*/ +WebGUI.AssetManager.initSearch += function () { + + WebGUI.AssetManager.initMoreMenus(); + + +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.removeHighlightFromRow ( child ) + Remove the highlight from a row by removing the "highlight" class. +*/ +WebGUI.AssetManager.removeHighlightFromRow += function ( child ) { + var row = WebGUI.AssetManager.findRow( child ); + if ( YAHOO.util.Dom.hasClass( row, "highlight" ) ) { + YAHOO.util.Dom.removeClass( row, "highlight" ); + } +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.selectRow ( child ) + Check the assetId checkbox in the row that contains the given child. + Used when something in the row changes. +*/ +WebGUI.AssetManager.selectRow += function ( child ) { + // First find the row + var node = WebGUI.AssetManager.findRow( child ); + WebGUI.AssetManager.addHighlightToRow( child ); + + // Now find the assetId checkbox in the first element + var inputs = node.getElementsByTagName( "input" ); + for ( var i = 0; i < inputs.length; i++ ) { + if ( inputs[i].name == "assetId" ) { + inputs[i].checked = true; + break; + } + } +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.showMoreMenu ( event, element ) + Show the more menu located inside element. +*/ +WebGUI.AssetManager.showMoreMenu += function ( event, element ) { + if ( element.menuTimeout ) { + clearTimeout( element.menuTimeout ); + } + var ul = element.getElementsByTagName( "ul" )[0]; + ul.style.display = "block"; +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.toggleHighlightForRow ( checkbox ) + Toggle the highlight for the row based on the state of the checkbox +*/ +WebGUI.AssetManager.toggleHighlightForRow += function ( checkbox ) { + if ( checkbox.checked ) { + WebGUI.AssetManager.addHighlightToRow( checkbox ); + } + else { + WebGUI.AssetManager.removeHighlightFromRow( checkbox ); + } +}; + +/*--------------------------------------------------------------------------- + WebGUI.AssetManager.toggleRow ( child ) + Toggles the entire row by finding the checkbox and doing what needs to be + done. +*/ +WebGUI.AssetManager.toggleRow += function ( child ) { + var row = WebGUI.AssetManager.findRow( child ); + + // Find the checkbox + var inputs = row.getElementsByTagName( "input" ); + for ( var i = 0; i < inputs.length; i++ ) { + if ( inputs[i].name == "assetId" ) { + inputs[i].checked = inputs[i].checked + ? false + : true + ; + WebGUI.AssetManager.toggleHighlightForRow( inputs[i] ); + break; + } + } +}; + diff --git a/www/extras/yui-webgui/build/form/form.js b/www/extras/yui-webgui/build/form/form.js index ff32a4683..200ef4498 100644 --- a/www/extras/yui-webgui/build/form/form.js +++ b/www/extras/yui-webgui/build/form/form.js @@ -30,7 +30,10 @@ WebGUI.Form.toggleAllCheckboxesInForm if (checkboxesName && input.name != checkboxesName) continue; + // Change the state input.checked = state; + // Run the appropriate scripts + input.onchange(); } // Update the saved state