package WebGUI::Asset; =head1 LEGAL ------------------------------------------------------------------- WebGUI is Copyright 2001-2009 Plain Black Corporation. ------------------------------------------------------------------- Please read the legal notices (docs/legal.txt) and the license (docs/license.txt) that came with this distribution before using this software. ------------------------------------------------------------------- http://www.plainblack.com info@plainblack.com ------------------------------------------------------------------- =cut use strict; use WebGUI::Asset::Shortcut; =head1 NAME Package WebGUI::Asset (AssetTrash) =head1 DESCRIPTION This is a mixin package for WebGUI::Asset that contains all trash related functions. =head1 SYNOPSIS use WebGUI::Asset; =head1 METHODS These methods are available from this class: =cut #------------------------------------------------------------------- =head2 getAssetsInTrash ( [limitToUser,userId] ) Returns an array reference of title, assetId, and classname to the assets in the Trash. =head3 limitToUser If True, only return assets last updated by userId. =head3 userId If not specified, uses current user. =cut sub getAssetsInTrash { my $self = shift; my $limitToUser = shift; my $userId = shift || $self->session->user->userId; my @assets; my $limit; if ($limitToUser) { $limit = "and asset.stateChangedBy=".$self->session->db->quote($userId); } my $sth = $self->session->db->read(" select asset.assetId, assetData.revisionDate, asset.className from asset left join assetData on asset.assetId=assetData.assetId where asset.state='trash' and assetData.revisionDate=(SELECT max(revisionDate) from assetData where assetData.assetId=asset.assetId) $limit group by assetData.assetId order by assetData.title desc "); while (my ($id, $date, $class) = $sth->array) { my $asset = WebGUI::Asset->new($self->session,$id,$class,$date); if ($asset){ push(@assets, $asset); }else{ $self->session->errorHandler->error("AssetTrash::getAssetsInTrash - failed to instanciate asset with assetId $id, className $class, and revisionDate $date"); } } $sth->finish; return \@assets; } #---------------------------------------------------------------------------- =head2 isInTrash ( ) Returns true if the asset is in the trash. =cut sub isInTrash { my $self = shift; return $self->get("state") eq "trash"; } #------------------------------------------------------------------- =head2 purge ( [ options ] ) Deletes an asset from tables and removes anything bound to that asset, including descendants. Returns 1 on success and 0 on failure. =head3 options A hash refernece containing options that change the behavior of this method. =head4 skipExported A boolean that, if true, will skip dealing with exported files. =head4 outputSub A subroutine used to report the status of the purge, most likely used by WebGUI::ProgressBar->update. =cut sub purge { my $self = shift; my $options = shift; my $session = $self->session; my $outputSub = $options->{outputSub} || sub {}; my $i18n = WebGUI::International->new($session, 'Asset'); # can't delete if it's one of these things if ($self->getId eq $session->setting->get("defaultPage") || $self->getId eq $session->setting->get("notFoundPage") || $self->get("isSystem")) { $outputSub->(sprintf $i18n->get('Trying to delete system page %s. Aborting'), $self->getTitle); $session->errorHandler->security("delete a system protected page (".$self->getId.")"); return 0; } # assassinate the offspring my $kids = $self->getLineage(["children"],{ returnObjects => 1, statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)], statusToInclude => [qw(approved archived pending)], }); foreach my $kid (@{$kids}) { # Technically get lineage should never return an undefined object from getLineage when called like this, but it did so this saves the world from destruction. if (defined $kid) { unless ($kid->purge) { $self->errorHandler->security("delete one of (".$self->getId.")'s children which is a system protected page"); $outputSub->(sprintf $i18n->get('Trying to delete system page %s. Aborting purge'), $self->getTitle); return 0; } } else { $outputSub->($i18n->get('Undefined child')); $session->errorHandler->error("getLineage returned an undefined object in the AssetTrash->purge method. Unable to purge asset."); } } # Delete shortcuts to this asset # Also publish any shortcuts to this asset that are in the trash $outputSub->($i18n->get('Purging shortcuts')); my $shortcuts = WebGUI::Asset::Shortcut->getShortcutsForAssetId($self->session, $self->getId, { returnObjects => 1, }); for my $shortcut ( @$shortcuts ) { $shortcut->purge({ outputSub => $outputSub, }); } # gotta delete stuff we've exported unless ($options->{skipExported}) { $outputSub->($i18n->get('Deleting exported files')); $self->_invokeWorkflowOnExportedFiles($session->setting->get('purgeWorkflow'), 1); } # gonna need this at the end my $tagId = $self->get("tagId"); # clean up keywords $outputSub->($i18n->get('Deleting keywords')); WebGUI::Keyword->new($session)->deleteKeywordsForAsset($self); # clean up search engine $outputSub->($i18n->get('Clearing search index')); WebGUI::Search::Index->new($self)->delete; # clean up cache $outputSub->($i18n->get('Clearing cache')); WebGUI::Cache->new($session)->deleteChunk(["asset",$self->getId]); $self->purgeCache; # delete stuff out of the asset tables $outputSub->($i18n->get('Clearing asset tables')); $session->db->beginTransaction; $session->db->write("delete from metaData_values where assetId = ?",[$self->getId]); foreach my $definition (@{$self->definition($session)}) { $session->db->write("delete from ".$definition->{tableName}." where assetId=?", [$self->getId]); } $session->db->write("delete from asset where assetId=?", [$self->getId]); $session->db->commit; # log that we've purged this asset $self->updateHistory("purged"); $self = undef; # clean up version tag if empty my $versionTag = WebGUI::VersionTag->new($session, $tagId); if ($versionTag && $versionTag->getAssetCount == 0) { $versionTag->rollback; } return 1; } #------------------------------------------------------------------- =head2 trash ( $options ) Removes asset from lineage, places it in trash state. The "gap" in the lineage is changed in state to trash-limbo. =head3 $options An optional hashref of options =head4 outputSub A subroutine used to report the status of the purge, most likely used by WebGUI::ProgressBar->update. =cut sub trash { my $self = shift; my $options = shift; my $session = $self->session; my $outputSub = $options->{outputSub} || sub {}; my $i18n = WebGUI::International->new($session, 'Asset'); if ($self->getId eq $session->setting->get("defaultPage") || $self->getId eq $session->setting->get("notFoundPage") || $self->get('isSystem')) { $outputSub->(sprintf $i18n->get('Trying to delete system page %s. Aborting'), $self->getTitle); $session->errorHandler->security("delete a system protected page (".$self->getId.")"); return undef; } $outputSub->($i18n->get('Deleting exported files')); foreach my $asset ($self, @{$self->getLineage(['descendants'], {returnObjects => 1})}) { $asset->_invokeWorkflowOnExportedFiles($session->setting->get('trashWorkflow'), 1); } # Trash any shortcuts to this asset my $shortcuts = WebGUI::Asset::Shortcut->getShortcutsForAssetId($session, $self->getId, { returnObjects => 1}); $outputSub->($i18n->get('Purging shortcuts')); for my $shortcut ( @$shortcuts ) { $shortcut->trash({ outputSub => $outputSub, }); } # Raw database work is more efficient than $asset->update my $db = $session->db; $db->beginTransaction; my $sth = $db->read("select assetId from asset where lineage like ?",[$self->get("lineage").'%']); $outputSub->($i18n->get('Clearing search index')); while (my ($id) = $sth->array) { $db->write("delete from assetIndex where assetId=?",[$id]); } $outputSub->($i18n->get('Clearing asset tables')); $db->write("update asset set state='trash-limbo' where lineage like ?",[$self->get("lineage").'%']); $db->write("update asset set state='trash', stateChangedBy=?, stateChanged=? where assetId=?",[$session->user->userId, $session->datetime->time(), $self->getId]); $db->commit; # Update ourselves since we didn't use update() $self->{_properties}{state} = "trash"; $self->updateHistory("trashed"); $self->purgeCache; } require WebGUI::Workflow::Activity::DeleteExportedFiles; sub _invokeWorkflowOnExportedFiles { my $self = shift; my $workflowId = shift; my $clearExportedAs = shift; if ($clearExportedAs) { $self->session->db->write("UPDATE asset SET lastExportedAs = NULL WHERE assetId = ?", [$self->getId]); } if ($workflowId) { my ($lastExportedAs) = $self->get("lastExportedAs"); my $wfInstance = WebGUI::Workflow::Instance->create($self->session, { workflowId => $workflowId }); $wfInstance->setScratch( WebGUI::Workflow::Activity::DeleteExportedFiles::DELETE_FILES_SCRATCH() => Storable::freeze([ defined($lastExportedAs) ? ($lastExportedAs) : () ]) ); $wfInstance->start(1); } } #------------------------------------------------------------------- =head2 www_delete Moves self to trash, returns www_view() method of Parent if canEdit. Otherwise returns AdminConsole rendered insufficient privilege. =cut sub www_delete { my $self = shift; return $self->session->privilege->insufficient() unless ($self->canEdit && $self->canEditIfLocked); return $self->session->privilege->vitalComponent() if $self->get('isSystem'); return $self->session->privilege->vitalComponent() if (isIn($self->getId, $self->session->setting->get("defaultPage"), $self->session->setting->get("notFoundPage"))); $self->trash; $self->session->asset($self->getParent); return $self->getParent->www_view; } #------------------------------------------------------------------- =head2 www_deleteList Moves list of assets to trash, returns www_manageAssets() method of self if canEdit. Otherwise returns AdminConsole rendered insufficient privilege. =cut sub www_deleteList { my $self = shift; my $session = $self->session; my $pb = WebGUI::ProgressBar->new($session); my $i18n = WebGUI::International->new($session, 'Asset'); my $form = $session->form; my @assetIds = $form->param('assetId'); $pb->start($i18n->get('Delete Assets'), $session->url->extras('adminConsole/assets.gif')); ASSETID: foreach my $assetId (@assetIds) { my $asset = eval { WebGUI::Asset->newPending($session,$assetId); }; if ($@) { $pb->update(sprintf $i18n->get('Error getting asset with assetId %s'), $assetId); next ASSETID; } if (! ($asset->canEdit && $asset->canEditIfLocked) ) { $pb->update(sprintf $i18n->get('You cannot edit the asset %s, skipping'), $asset->getTitle); } else { $asset->trash({outputSub => sub { $pb->update(@_); } }); } } my $method = ($session->form->process("proceed")) ? $session->form->process('proceed') : 'manageTrash'; $pb->finish($self->getUrl('func='.$method)); } #------------------------------------------------------------------- =head2 www_manageTrash ( ) Returns an AdminConsole to deal with assets in the Trash. If user isn't in the Turn On Admin group, renders an insufficient privilege page. =cut sub www_manageTrash { my $self = shift; my $ac = WebGUI::AdminConsole->new($self->session,"trash"); my $i18n = WebGUI::International->new($self->session,"Asset"); return $self->session->privilege->insufficient() unless ($self->session->user->isInGroup(12)); my ($header, $limit); $ac->setHelp("trash manage"); if ($self->session->form->process("systemTrash") && $self->session->user->isAdmin) { $header = $i18n->get(965); $ac->addSubmenuItem($self->getUrl('func=manageTrash'), $i18n->get(10,"WebGUI")); } else { $ac->addSubmenuItem($self->getUrl('func=manageTrash;systemTrash=1'), $i18n->get(964)); $limit = 1; } $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"}); my $output = "
 
'; return $ac->render($output, $header); } #------------------------------------------------------------------- =head2 www_purgeList ( ) Purges a piece of content, including all it's revisions, from the system permanently. =cut sub www_purgeList { my $self = shift; my $session = $self->session; my $pb = WebGUI::ProgressBar->new($session); my $i18n = WebGUI::International->new($session, 'Asset'); $pb->start($i18n->get('purge'), $session->url->extras('adminConsole/assets.gif')); ASSETID: foreach my $id ($session->form->param("assetId")) { my $asset = eval { WebGUI::Asset->newPending($session,$id); }; if ($@) { $pb->update(sprintf $i18n->get('Error getting asset with assetId %s'), $id); next ASSETID; } if (! $asset->canEdit) { $pb->update(sprintf $i18n->get('You cannot edit the asset %s, skipping'), $asset->getTitle); } else { $asset->purge({outputSub => sub { $pb->update(@_); } }); } } my $method = ($session->form->process("proceed")) ? $session->form->process('proceed') : 'manageTrash'; $pb->finish($self->getUrl('func='.$method)); } #------------------------------------------------------------------- =head2 www_restoreList ( ) Restores a piece of content from the trash back to it's original location. =cut sub www_restoreList { my $self = shift; foreach my $id ($self->session->form->param("assetId")) { my $asset = WebGUI::Asset->newByDynamicClass($self->session,$id); $asset->publish if $asset->canEdit; } if ($self->session->form->process("proceed") ne "") { my $method = "www_".$self->session->form->process("proceed"); return $self->$method(); } return $self->www_manageTrash(); } 1;