diff --git a/docs/changelog/6.x.x.txt b/docs/changelog/6.x.x.txt
index f4c24d661..14630eaa6 100644
--- a/docs/changelog/6.x.x.txt
+++ b/docs/changelog/6.x.x.txt
@@ -28,6 +28,8 @@
- Added archive/unarchive options to CS threads.
- Added a mechanism to send emails to a CS which will accept them as posts.
- Increased the performance of CS Thread viewing by 500%.
+ - Added a mechanism for importing and exporting packages. This is useful for
+ trading styles and other content between sites.
- Added a database cache option as an alternative to memcached.
- Removed page caching system and added individual asset caches, because not
everything should cache in the same way.
diff --git a/lib/WebGUI/AdminConsole.pm b/lib/WebGUI/AdminConsole.pm
index e816a51a0..1aed01dcd 100644
--- a/lib/WebGUI/AdminConsole.pm
+++ b/lib/WebGUI/AdminConsole.pm
@@ -251,15 +251,6 @@ sub getAdminFunction {
op=>"editSettings",
group=>"3"
},
- # "themes"=>{
- # title=>{
- # id=>"themes",
- # namespace=>"WebGUI"
- # },
- # icon=>"themes.gif",
- # op=>"listThemes",
- # group=>"9"
- # },
"help"=>{
title=>{
id=>"help",
diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm
index b084a8016..e27e215af 100644
--- a/lib/WebGUI/Asset.pm
+++ b/lib/WebGUI/Asset.pm
@@ -1107,8 +1107,8 @@ sub manageAssets {
$output .= "assetManager.AddLineSortData('','','','".$title."','".$child->getName
."','".$child->get("revisionDate")."','".$child->get("assetSize")."');
assetManager.addAssetMetaData('".$child->getUrl."', '".$child->getRank."', '".$title."');\n";
- $self->session->output->print($output);
- $output = '';
+ $self->session->output->print($output,1);
+ $output = '';
}
$output .= '
assetManager.AddButton("'.$i18n->get("delete").'","deleteList","manageAssets");
@@ -1127,7 +1127,7 @@ sub manageAssets {
'.$i18n->get(1083).' ';
- $self->session->output->print($output);
+ $self->session->output->print($output,1);
$output = '';
foreach my $link (@{$self->getAssetAdderLinks("proceed=manageAssets","assetContainers")}) {
$output .= '
@@ -1136,8 +1136,6 @@ sub manageAssets {
$output .= ' ';
}
$output .= ' ';
- $self->session->output->print($output);
- $output = '';
foreach my $link (@{$self->getAssetAdderLinks("proceed=manageAssets")}) {
$output .= '
'.$link->{label}.' ';
@@ -1145,8 +1143,6 @@ sub manageAssets {
$output .= ' ';
}
$output .= ' ';
- $self->session->output->print($output);
- $output = '';
foreach my $link (@{$self->getAssetAdderLinks("proceed=manageAssets","utilityAssets")}) {
$output .= '
'.$link->{label}.' ';
@@ -1186,8 +1182,7 @@ sub manageAssets {
';
}
$self->session->output->print($output);
- $output = '';
- $output .= ' '.$i18n->get("packages").' ';
+ $output = '
';
$self->session->output->print($output);
- $output = '';
- return $output;
+ return undef;
}
#-------------------------------------------------------------------
@@ -1279,7 +1278,7 @@ sub manageAssetsSearch {
$output .= "assetManager.AddLineSortData('','','','".$title."','".$child->getName
."','".$child->get("revisionDate")."','".$child->get("assetSize")."');
assetManager.addAssetMetaData('".$child->getUrl."', '".$child->getRank."', '".$title."');\n";
- $self->session->output->print($output);
+ $self->session->output->print($output,1);
$output = '';
}
$output .= 'assetManager.AddButton("'.$i18n->get("delete").'","deleteList","manageAssets");
@@ -1921,6 +1920,7 @@ sub www_manageAssets {
}
my $out = $self->getAdminConsole->render($self->getAdminConsole->render("~~~"));
my ($head, $foot) = split("~~~",$out);
+ $self->session->style->sent(1);
$self->session->http->getHeader;
$self->session->output->print($head);
$self->session->output->print('',1);
diff --git a/lib/WebGUI/Asset/Post/Thread.pm b/lib/WebGUI/Asset/Post/Thread.pm
index 6431bf88d..089327dfc 100644
--- a/lib/WebGUI/Asset/Post/Thread.pm
+++ b/lib/WebGUI/Asset/Post/Thread.pm
@@ -529,9 +529,7 @@ sub processPropertiesFromFormPost {
#-------------------------------------------------------------------
sub purge {
my $self = shift;
-$self->session->errorHandler->warn("Y");
- $self->session->db->write("delete from Thread_read where postId=?",[$self->getId]);
-$self->session->errorHandler->warn("Z");
+ $self->session->db->write("delete from Thread_read where threadId=?",[$self->getId]);
$self->SUPER::purge;
}
diff --git a/lib/WebGUI/AssetLineage.pm b/lib/WebGUI/AssetLineage.pm
index ec466da7d..91a999951 100644
--- a/lib/WebGUI/AssetLineage.pm
+++ b/lib/WebGUI/AssetLineage.pm
@@ -36,7 +36,7 @@ These methods are available from this class:
#-------------------------------------------------------------------
-=head2 addChild ( properties [, id ] )
+=head2 addChild ( properties [, id, revisionDate ] )
Adds a child asset to a parent. Creates a new AssetID for child. Makes the parent know that it has children. Adds a new asset to the asset table. Returns the newly created Asset.
@@ -48,12 +48,17 @@ A hash reference containing a list of properties to associate with the child. Th
A unique 22 character ID. By default WebGUI will generate this and you should almost never specify it. This is mainly here for developers that want to include default templates in their plug-ins.
+=head3 revisionDate
+
+An epoch representing the time this asset was created.
+
=cut
sub addChild {
my $self = shift;
my $properties = shift;
my $id = shift || $self->session->id->generate();
+ my $now = shift || $self->session->datetime->time();
# add a few things just in case the creator forgets
$properties->{ownerUserId} ||= '3';
$properties->{groupIdEdit} ||= '12';
@@ -63,7 +68,6 @@ sub addChild {
my $lineage = $self->get("lineage").$self->getNextChildRank;
$self->{_hasChildren} = 1;
$self->session->db->beginTransaction;
- my $now =$self->session->datetime->time();
$self->session->db->write("insert into asset (assetId, parentId, lineage, creationDate, createdBy, className, state) values (?,?,?,?,?,?,'published')",
[$id,$self->getId,$lineage,$now,$self->session->user->userId,$properties->{className}]);
my $temp = WebGUI::Asset->newByPropertyHashRef($self->session,{
diff --git a/lib/WebGUI/AssetPackage.pm b/lib/WebGUI/AssetPackage.pm
index 3f0fe100f..097112e5b 100644
--- a/lib/WebGUI/AssetPackage.pm
+++ b/lib/WebGUI/AssetPackage.pm
@@ -15,6 +15,8 @@ package WebGUI::Asset;
=cut
use strict;
+use JSON;
+use WebGUI::Storage;
=head1 NAME
@@ -35,6 +37,45 @@ These methods are available from this class:
=cut
+#-------------------------------------------------------------------
+
+=head2 exportAssetData ( )
+
+Converts all the properties of this asset into a hash reference and then returns the hash reference. This method should be expanded upon by sub classes that have more data than just asset property data. Two nodes will be created: "properties" and "storage". Properties is a hash reference of the asset properties. Storage is an array reference of storage location ids. Storage will initially be empty, but if you have storage locations you want to include in this, then please push their ids onto this list when you override this method.
+
+=cut
+
+sub exportData {
+ my $self = shift;
+ my %data = %{$self->get};
+ my %hash = ( properties => \%data, storage=>[] );
+ return \%hash;
+}
+
+#-------------------------------------------------------------------
+
+=head2 exportPackage ( )
+
+Turns this package into a package file and returns the storage location object of the package file.
+
+=cut
+
+sub exportPackage {
+ my $self = shift;
+ my $storage = WebGUI::Storage->createTemp($self->session);
+ foreach my $asset (@{$self->getLineage(["self","descendants"],{returnObjects=>1})}) {
+ my $data = $asset->exportData;
+ $storage->addFileFromScalar($data->{properties}{lineage}.".json", JSON::objToJson($data));
+ foreach my $storageId (@{$data->{storage}}) {
+ my $assetStorage = WebGUI::Storage->get($self->session, $storageId);
+ $assetStorage->tar($storageId.".storage", $storage);
+ }
+ }
+ my $filename = $self->get("url").".wgpkg";
+ $filename =~ s/\//_/g;
+ return $storage->tar($filename);
+}
+
#-------------------------------------------------------------------
=head2 getPackageList ( )
@@ -63,7 +104,7 @@ sub getPackageList {
$sql .= ")) and asset.state='published' group by assetData.assetId order by assetData.title desc";
my $sth = $self->session->db->read($sql);
while (my ($id, $date, $class) = $sth->array) {
- my $asset = WebGUI::Asset->new($id,$class);
+ my $asset = WebGUI::Asset->new($self->session, $id,$class);
push(@assets, $asset) if ($asset->get("isPackage"));
}
$sth->finish;
@@ -71,6 +112,64 @@ sub getPackageList {
}
+#-------------------------------------------------------------------
+
+=head2 importAssetData ( hashRef )
+
+Imports the data exported by the exportAssetData method. If the asset already exists, a new revision will be created with these properties. If it doesn't exist then a child will be added to the current asset. Returns a reference to the created asset.
+
+=head3 hashRef
+
+A hash reference containing the exported data.
+
+=cut
+
+sub importAssetData {
+ my $self = shift;
+ my $data = shift;
+ my $id = $data->{properties}{assetId};
+ my $class = $data->{properties}{className};
+ my $version = $data->{properties}{revisionDate};
+ my $asset = WebGUI::Asset->new($self->session, $id, $class, $version);
+ if (defined $asset) { # update an existing revision
+ $asset->update($data->{properties});
+ } else {
+ $asset = WebGUI::Asset->new($self->session, $id, $class);
+ if (defined $asset) { # create a new revision of an existing asset
+ $asset->addRevision($data->{properties}, $version);
+ } else { # add an entirely new asset
+ $self->addChild($data->{properties}, $id, $version);
+ }
+ }
+ return $asset;
+}
+
+#-------------------------------------------------------------------
+
+=head2 importPackage ( storageLocation )
+
+Imports the data from a webgui package file.
+
+=head3 storageLocation
+
+A reference to a WebGUI::Storage object that contains a webgui package file.
+
+=cut
+
+sub importPackage {
+ my $self = shift;
+ my $storage = shift;
+ my $decompressed = $storage->untar($storage->getFiles->[0]);
+ foreach my $file (@{$decompressed->getFiles}) {
+ my $data = JSON::jsonToObj($decompressed->getFileContentsAsScalar($file));
+ foreach my $storageId (@{$data->{storage}}) {
+ my $assetStorage = WebGUI::Storage->get($self->session, $storageId);
+ $decompressed->untar($storageId.".storage", $assetStorage);
+ }
+ $self->importAssetData($data);
+ }
+}
+
#-------------------------------------------------------------------
=head2 www_deployPackage ( )
@@ -100,6 +199,36 @@ sub www_deployPackage {
return "";
}
+#-------------------------------------------------------------------
+
+=head2 www_exportPackage ( )
+
+Returns a tarball file for the user to download containing the package data.
+
+=cut
+
+sub www_exportPackage {
+ my $self = shift;
+ return $self->session->privilege->insufficient() unless ($self->get("isPackage") && $self->canEdit && $self->session->user->isInGroup(4));
+ my $storage = $self->exportPackage;
+ $self->session->http->setRedirect($storage->getUrl($storage->getFiles->[0]));
+ return "redirect";
+}
+
+#-------------------------------------------------------------------
+
+=head2 www_importPackage ( )
+
+=cut
+
+sub www_importPackage {
+ my $self = shift;
+ return $self->session->privilege->insufficient() unless ($self->canEdit && $self->session->user->isInGroup(4));
+ my $storage = WebGUI::Storage->createTemp($self->session);
+ $storage->addFileFromFormPost("packageFile",1);
+ $self->importPackage($storage) if ($storage->getFileExtension($storage->getFiles->[0]) eq "wgpkg");
+ return $self->www_manageAssets();
+}
1;
diff --git a/lib/WebGUI/Operation.pm b/lib/WebGUI/Operation.pm
index 55328f9cc..0d45c8efc 100644
--- a/lib/WebGUI/Operation.pm
+++ b/lib/WebGUI/Operation.pm
@@ -185,20 +185,6 @@ sub getOperations {
'makePrintable' => 'WebGUI::Operation::Style',
'setPersonalStyle' => 'WebGUI::Operation::Style',
'unsetPersonalStyle' => 'WebGUI::Operation::Style',
- 'viewTheme' => 'WebGUI::Operation::Theme',
- 'deleteThemeComponent' => 'WebGUI::Operation::Theme',
- 'deleteThemeComponentConfirm' => 'WebGUI::Operation::Theme',
- 'importTheme' => 'WebGUI::Operation::Theme',
- 'importThemeValidate' => 'WebGUI::Operation::Theme',
- 'importThemeSave' => 'WebGUI::Operation::Theme',
- 'exportTheme' => 'WebGUI::Operation::Theme',
- 'addThemeComponent' => 'WebGUI::Operation::Theme',
- 'addThemeComponentSave' => 'WebGUI::Operation::Theme',
- 'deleteTheme' => 'WebGUI::Operation::Theme',
- 'deleteThemeConfirm' => 'WebGUI::Operation::Theme',
- 'editTheme' => 'WebGUI::Operation::Theme',
- 'editThemeSave' => 'WebGUI::Operation::Theme',
- 'listThemes' => 'WebGUI::Operation::Theme',
'editUserKarma' => 'WebGUI::Operation::User',
'editUserKarmaSave' => 'WebGUI::Operation::User',
'deleteGrouping' => 'WebGUI::Operation::Group',
diff --git a/lib/WebGUI/Session/Style.pm b/lib/WebGUI/Session/Style.pm
index ed81191bd..cb9b77b33 100644
--- a/lib/WebGUI/Session/Style.pm
+++ b/lib/WebGUI/Session/Style.pm
@@ -80,30 +80,12 @@ sub generateAdditionalHeadTags {
my $tags = $self->{_raw};
# generate additional link tags
foreach my $url (keys %{$self->{_link}}) {
- $tags .= ' {_link}{$url}}) {
- $tags .= ' '.$name.'="'.$self->{_link}{$url}{$name}.'"';
- }
- $tags .= ' />'."\n";
+ $tags .= $self->{_link}{$url};
}
# generate additional javascript tags
- foreach my $tag (@{$self->{_javascript}}) {
- $tags .= ''."\n";
+ foreach my $url (keys %{$self->{_javascript}}) {
+ $tags .= $self->{_javascript}{$url};
}
- # generate additional meta tags
- foreach my $tag (@{$self->{_meta}}) {
- $tags .= ' {$name}.'"';
- }
- $tags .= ' />'."\n";
- }
- # append extraHeadTags
-# $tags .= $self->session->asset->getExtraHeadTags."\n" if ($self->session->asset);
delete $self->{_meta};
delete $self->{_raw};
delete $self->{_javascript};
@@ -275,7 +257,14 @@ sub setLink {
my $self = shift;
my $url = shift;
my $params = shift;
- $self->{_link}{$url} = $params;
+ return undef if ($self->{_link}{$url});
+ my $tag = ' {$name}.'"';
+ }
+ $tag .= '/>'."\n";
+ $self->{_link}{$url} = $tag;
+ $self->session->output->print($tag) if ($self->sent);
}
@@ -295,7 +284,12 @@ A hash reference containing the parameters of the meta tag.
sub setMeta {
my $self = shift;
my $params = shift;
- push(@{$self->{_meta}},$params);
+ my $tag = ' {$name}.'"';
+ }
+ $tag .= ' />'."\n";
+ $self->setRawHeadTags($tag);
}
@@ -316,6 +310,7 @@ sub setRawHeadTags {
my $self = shift;
my $tags = shift;
$self->{_raw} .= $tags;
+ $self->session->output->print($tags) if ($self->sent);
}
@@ -339,12 +334,14 @@ sub setScript {
my $self = shift;
my $url = shift;
my $params = shift;
- $params->{src} = $url;
- my $found = 0;
- foreach my $script (@{$self->{_javascript}}) {
- $found = 1 if ($script->{src} eq $url);
+ return undef if ($self->{_javascript}{$url});
+ my $tag = ''."\n";
+ $self->{_javascript}{$url} = $tag;
+ $self->session->output->print($tag) if ($self->sent);
}
#-------------------------------------------------------------------
diff --git a/lib/WebGUI/Storage.pm b/lib/WebGUI/Storage.pm
index 1ab601e9c..e6a552f73 100644
--- a/lib/WebGUI/Storage.pm
+++ b/lib/WebGUI/Storage.pm
@@ -17,6 +17,7 @@ package WebGUI::Storage;
use Archive::Tar;
use File::Copy qw(cp);
use FileHandle;
+use File::Find;
use File::Path;
use POSIX;
use Storable qw(nstore retrieve);
@@ -38,6 +39,7 @@ This package provides a mechanism for storing and retrieving files that are not
use WebGUI::Storage;
$store = WebGUI::Storage->create($self->session);
+ $store = WebGUI::Storage->createTemp($self->session);
$store = WebGUI::Storage->get($self->session,$id);
$filename = $store->addFileFromFilesystem($pathToFile);
@@ -723,7 +725,7 @@ sub setPrivileges {
#-------------------------------------------------------------------
-=head2 tar ( filename )
+=head2 tar ( filename [, storage ] )
Archives this storage location into a tar file and then compresses it with a zlib algorithm. It then returns a new WebGUI::Storage object for the archive.
@@ -731,27 +733,33 @@ Archives this storage location into a tar file and then compresses it with a zli
The name of the tar file to be created. Should ideally end with ".tar.gz".
+=head3 storage
+
+Pass in a storage location object to create the tar file in, instead of having a temporary one be created. Note that it cannot be the same location that's being tarred.
+
=cut
sub tar {
my $self = shift;
my $filename = shift;
+ my $temp = shift || WebGUI::Storage->createTemp($self->session);
chdir $self->getPath;
- my $temp = WebGUI::Node->create;
+ my @files = ();
+ find(sub { push(@files, $File::Find::name)}, ".");
if ($Archive::Tar::VERSION eq '0.072') {
my $tar = Archive::Tar->new();
- $tar->add_files($self->getFiles);
+ $tar->add_files(@files);
$tar->write($temp->getPath($filename),1);
} else {
- Archive::Tar->create_archive($temp->getPath($filename),1,$self->getFiles);
+ Archive::Tar->create_archive($temp->getPath($filename),1,@files);
}
return $temp;
}
#-------------------------------------------------------------------
-=head2 untar ( filename )
+=head2 untar ( filename [, storage ] )
Unarchives a file into a new storage location. Returns the new WebGUI::Storage object.
@@ -759,15 +767,20 @@ Unarchives a file into a new storage location. Returns the new WebGUI::Storage o
The name of the tar file to be untarred.
+=head3 storage
+
+Pass in a storage location object to extract the contents to, instead of having a temporary one be created.
+
=cut
sub untar {
my $self = shift;
my $filename = shift;
- my $temp = WebGUI::Node->create;
+ my $temp = shift || WebGUI::Storage->createTemp($self->session);
chdir $temp->getPath;
Archive::Tar->extract_archive($self->getPath($filename),1);
$self->_addError(Archive::Tar->error) if (Archive::Tar->error);
+ return $temp;
}
diff --git a/lib/WebGUI/i18n/English/Asset.pm b/lib/WebGUI/i18n/English/Asset.pm
index c3ae43f61..ee4a9a488 100644
--- a/lib/WebGUI/i18n/English/Asset.pm
+++ b/lib/WebGUI/i18n/English/Asset.pm
@@ -1,6 +1,12 @@
package WebGUI::i18n::English::Asset;
our $I18N = {
+ 'import' => {
+ message => q|Import|,
+ lastUpdated => 0,
+ context => q|the title on the package import button|
+ },
+
'over max assets' => {
message => q|Your administrator has limited the number of assets you may place on your site, and you have exceeded the limit. Delete some old assets in order to add more.|,
lastUpdated => 0,