diff --git a/lib/WebGUI/Content/Shop.pm b/lib/WebGUI/Content/Shop.pm
index 70019ef61..9ff3e8b92 100644
--- a/lib/WebGUI/Content/Shop.pm
+++ b/lib/WebGUI/Content/Shop.pm
@@ -200,8 +200,9 @@ sub www_products {
my $session = shift;
my $output = undef;
my $method = "www_".$session->form->get("method");
+ my $products = WebGUI::Shop::Products->new($session);
if ($method ne "www_" && WebGUI::Shop::Products->can($method)) {
- $output = $WebGUI::Shop::Products->$method($session);
+ $output = $products->$method();
}
else {
WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $method", method=>$method);
diff --git a/lib/WebGUI/Shop/Admin.pm b/lib/WebGUI/Shop/Admin.pm
index eb412a7ec..a468579a0 100644
--- a/lib/WebGUI/Shop/Admin.pm
+++ b/lib/WebGUI/Shop/Admin.pm
@@ -69,6 +69,7 @@ sub getAdminConsole {
$ac->addSubmenuItem($url->page("shop=transaction;method=manage"), $i18n->get("transactions"));
$ac->addSubmenuItem($url->page("shop=vendor;method=manage"), $i18n->get("vendors"));
$ac->addSubmenuItem($url->page("shop=credit;method=manage"), $i18n->get("in shop credit"));
+ $ac->addSubmenuItem($url->page("shop=products;method=manage"), $i18n->get("products"));
return $ac;
}
diff --git a/lib/WebGUI/Shop/Products.pm b/lib/WebGUI/Shop/Products.pm
index bb5585f7b..7e8739522 100644
--- a/lib/WebGUI/Shop/Products.pm
+++ b/lib/WebGUI/Shop/Products.pm
@@ -2,6 +2,8 @@ package WebGUI::Shop::Products;
use strict;
+use Class::InsideOut qw{ :std };
+
use WebGUI::Text;
use WebGUI::Storage;
use WebGUI::Exception::Shop;
@@ -25,9 +27,11 @@ These subroutines are available from this package:
=cut
+readonly session => my %session;
+
#-------------------------------------------------------------------
-=head2 exportProducts ( $session )
+=head2 exportProducts ( )
Export all products from the WebGUI system in a CSV file. For details
about the file format, see importProducts.
@@ -38,7 +42,8 @@ file will be named siteProductData.csv.
=cut
sub exportProducts {
- my $session = shift;
+ my $self = shift;
+ my $session = $self->session;
my @columns = qw{sku shortdescription price weight quantity};
my $productData = WebGUI::Text::joinCSV(qw{mastersku title}, @columns) . "\n";
@columns = map { $_ eq 'shortdescription' ? 'shortdesc' : $_ } @columns;
@@ -60,7 +65,7 @@ sub exportProducts {
#-------------------------------------------------------------------
-=head2 importProducts ( $session, $filePath )
+=head2 importProducts ( $filePath )
Import products into the WebGUI system. If the master sku of a product
exists in the system, it will be updated. If master skus do not exist,
@@ -113,8 +118,9 @@ if old data has been deleted and new has been inserted.
=cut
sub importProducts {
- my $session = shift;
+ my $self = shift;
my $filePath = shift;
+ my $session = $self->session;
WebGUI::Error::InvalidParam->throw(error => q{Must provide the path to a file})
unless $filePath;
WebGUI::Error::InvalidFile->throw(error => qq{File could not be found}, brokenFile => $filePath)
@@ -209,6 +215,24 @@ sub importProducts {
#-------------------------------------------------------------------
+=head2 new ( $session )
+
+Constructor for the WebGUI::Shop::Products. Returns a WebGUI::Shop::Products object.
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $session = shift;
+ my $self = {};
+ bless $self, $class;
+ register $self;
+ $session{ id $self } = $session;
+ return $self;
+}
+
+#-------------------------------------------------------------------
+
=head2 www_exportProducts ( )
Export all product SKUs as a CSV file. Returns a WebGUI::Storage
@@ -223,7 +247,7 @@ sub www_exportProducts {
return $session->privilege->insufficient
unless $admin->canManage;
my $storage = $self->exportProducts();
- $self->session->http->setRedirect($storage->getUrl($storage->getFiles->[0]));
+ $session->http->setRedirect($storage->getUrl($storage->getFiles->[0]));
return "redirect";
}
@@ -237,29 +261,53 @@ or alter existing products.
=cut
sub www_importProducts {
- my $self = shift;
+ my $self = shift;
my $session = $self->session;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->insufficient
unless $admin->canManage;
my $storage = WebGUI::Storage->create($session);
my $productFile = $storage->addFileFromFormPost('importFile', 1);
- $self->importProducts($storage->getPath($productFile)) if $productFile;
- return $self->www_manage;
+ eval {
+ $self->importProducts($storage->getPath($productFile)) if $productFile;
+ };
+ my ($exception, $status_message);
+ if ($exception = Exception::Class->caught('WebGUI::Error::InvalidFile')) {
+ $status_message = sprintf 'A problem was found with your file: %s, %s',
+ $exception->brokenFile,
+ $exception->error;
+ if ($exception->brokenLine) {
+ $status_message .= sprintf ' on line %d', $exception->brokenLine;
+ }
+ }
+ elsif ($exception = Exception::Class->caught()) {
+ $status_message = sprintf 'A problem happened during the import: %s', $exception->error;
+ }
+ else {
+ my $i18n = WebGUI::International->new($session, 'Shop');
+ $status_message = $i18n->get('import successful');
+ }
+ return $self->www_manage($status_message);
}
#-------------------------------------------------------------------
-=head2 www_manage ( )
+=head2 www_manage ( $status_message )
User interface to synchronize product data. Provides an interface for
exporting all products on the site, and importing sets of products.
+=head3 $status_message
+
+An status message generated when import or export is called that needs to be
+displayed back to the user.
+
=cut
sub www_manage {
- my $self = shift;
- my $session = $self->session;
+ my $self = shift;
+ my $status_message = shift;
+ my $session = $self->session;
my $admin = WebGUI::Shop::Admin->new($session);
return $session->privilege->insufficient
unless $admin->canManage;
@@ -277,7 +325,14 @@ sub www_manage {
. q{}
. WebGUI::Form::formFooter($session);
- my $output =sprintf <%s
+EODIV
+ }
+
+ $output .= sprintf <%s%s
EODIV
diff --git a/lib/WebGUI/i18n/English/Shop.pm b/lib/WebGUI/i18n/English/Shop.pm
index 8e5c52f42..e62888123 100644
--- a/lib/WebGUI/i18n/English/Shop.pm
+++ b/lib/WebGUI/i18n/English/Shop.pm
@@ -908,6 +908,13 @@ our $I18N = {
lastUpdated => 1212550978,
context => q|Label for taking data out of the Shop (Tax, Product, etc.)|,
},
+
+ 'import successful' => {
+ message => q|Your products have been imported.|,
+ lastUpdated => 1213047491,
+ context => q|Message telling the user the their products have been imported successfully.|
+ },
+
};
1;
diff --git a/t/Shop/Products.t b/t/Shop/Products.t
index 20b68f1c6..ab441bc31 100644
--- a/t/Shop/Products.t
+++ b/t/Shop/Products.t
@@ -44,6 +44,8 @@ my $loaded = use_ok('WebGUI::Shop::Products');
my $storage;
my ($e, $failure);
+my $products = WebGUI::Shop::Products->new($session);
+
SKIP: {
skip 'Unable to load module WebGUI::Shop::Products', $tests unless $loaded;
@@ -56,12 +58,12 @@ SKIP: {
my $importNode = WebGUI::Asset::Sku::Product->getProductImportNode($session);
- eval { WebGUI::Shop::Products::importProducts($session); };
+ eval { $products->importProducts(); };
$e = Exception::Class->caught();
isa_ok($e, 'WebGUI::Error::InvalidParam', 'importProducts: error handling for an undefined path to file');
is($e->error, 'Must provide the path to a file', 'importProducts: error handling for an undefined path to file');
- eval { WebGUI::Shop::Products::importProducts($session, '/path/to/nowhere'); };
+ eval { $products->importProducts('/path/to/nowhere'); };
$e = Exception::Class->caught();
isa_ok($e, 'WebGUI::Error::InvalidFile', 'importProducts: error handling for file that does not exist in the filesystem');
is($e->error, 'File could not be found', 'importProducts: error handling for file that does not exist in the filesystem');
@@ -82,7 +84,7 @@ SKIP: {
my $originalChmod = (stat $productsFile)[2];
chmod oct(0000), $productsFile;
- eval { WebGUI::Shop::Products::importProducts($session, $productsFile); };
+ eval { $products->importProducts($productsFile); };
$e = Exception::Class->caught();
isa_ok($e, 'WebGUI::Error::InvalidFile', 'importProducts: error handling for file that cannot be read');
is($e->error, 'File is not readable', 'importProducts: error handling for file that that cannot be read');
@@ -99,8 +101,7 @@ SKIP: {
}
eval {
- $failure = WebGUI::Shop::Products::importProducts(
- $session,
+ $failure = $products->importProducts(
WebGUI::Test->getTestCollateralPath('productTables/missingHeaders.csv'),
);
};
@@ -117,8 +118,7 @@ SKIP: {
);
eval {
- $failure = WebGUI::Shop::Products::importProducts(
- $session,
+ $failure = $products->importProducts(
WebGUI::Test->getTestCollateralPath('productTables/badHeaders.csv'),
);
};
@@ -134,8 +134,7 @@ SKIP: {
'importProducts: error handling for a file with a missing header',
);
- my $pass = WebGUI::Shop::Products::importProducts(
- $session,
+ my $pass = $products->importProducts(
WebGUI::Test->getTestCollateralPath('productTables/goodProductTable.csv'),
);
ok($pass, 'Products imported');
@@ -195,15 +194,15 @@ SKIP: {
#
#######################################################################
- my $products = WebGUI::Shop::Products::exportProducts($session);
- isa_ok($products, 'WebGUI::Storage', 'exportProducts returns a Storage object');
- is(scalar @{ $products->getFiles }, 1, 'The storage contains just 1 file...');
- is(scalar $products->getFiles->[0], 'siteProductData.csv', '...with the correct filename');
- my $productData = $products->getFileContentsAsScalar($products->getFiles->[0]);
+ my $productsOut = $products->exportProducts();
+ isa_ok($productsOut, 'WebGUI::Storage', 'exportProducts returns a Storage object');
+ is(scalar @{ $productsOut->getFiles }, 1, 'The storage contains just 1 file...');
+ is(scalar $productsOut->getFiles->[0], 'siteProductData.csv', '...with the correct filename');
+ my $productData = $productsOut->getFileContentsAsScalar($productsOut->getFiles->[0]);
my @productData = split /\n/, $productData;
is(scalar @productData, 4, 'productData should have 4 entries, 1 header + 3 data');
is($productData[0], 'mastersku,title,sku,shortdescription,price,weight,quantity', 'header line is okay');
- my @productData = map { [ WebGUI::Text::splitCSV($_) ] } @productData[1..3];
+ @productData = map { [ WebGUI::Text::splitCSV($_) ] } @productData[1..3];
my ($sodas, $shirts);
foreach my $productData (@productData) {
if ($productData->[0] eq 'soda') {
@@ -229,17 +228,16 @@ SKIP: {
#
#######################################################################
- $pass = WebGUI::Shop::Products::importProducts(
- $session,
+ $pass = $products->importProducts(
WebGUI::Test->getTestCollateralPath('productTables/secondProductTable.csv'),
);
ok($pass, 'Products imported for the second time');
- my $count = $session->db->quickScalar('select count(*) from Product');
+ $count = $session->db->quickScalar('select count(*) from Product');
is($count, 3, 'three products were imported');
- my $soda = WebGUI::Asset::Sku->newBySku($session, 'soda');
- my $sodaCollateral = $soda->getAllCollateral('variantsJSON');
+ $soda = WebGUI::Asset::Sku->newBySku($session, 'soda');
+ $sodaCollateral = $soda->getAllCollateral('variantsJSON');
cmp_deeply(
$sodaCollateral,
[
@@ -256,7 +254,7 @@ SKIP: {
);
$shirt = WebGUI::Asset::Sku->newBySku($session, 't-shirt');
- my $shirtCollateral = $shirt->getAllCollateral('variantsJSON');
+ $shirtCollateral = $shirt->getAllCollateral('variantsJSON');
cmp_deeply(
$shirtCollateral,
[
@@ -304,19 +302,18 @@ SKIP: {
#
#######################################################################
- $pass = WebGUI::Shop::Products::importProducts(
- $session,
+ $pass = $products->importProducts(
WebGUI::Test->getTestCollateralPath('productTables/thirdProductTable.csv'),
);
ok($pass, 'Products imported for the third time');
- my $count = $session->db->quickScalar('select count(*) from Product');
+ $count = $session->db->quickScalar('select count(*) from Product');
is($count, 3, 'still have 3 products, nothing new added');
- my $soda = WebGUI::Asset::Sku->newBySku($session, 'soda');
+ $soda = WebGUI::Asset::Sku->newBySku($session, 'soda');
is($soda->getTitle(), 'Sweet Soda-totally organic', 'Title updated correctly for soda');
$shirt = WebGUI::Asset::Sku->newBySku($session, 't-shirt');
- my $shirtCollateral = $shirt->getAllCollateral('variantsJSON');
+ $shirtCollateral = $shirt->getAllCollateral('variantsJSON');
cmp_deeply(
$shirtCollateral,
[
@@ -340,8 +337,8 @@ SKIP: {
'collateral updated correctly for shirt'
);
- my $record = WebGUI::Asset::Sku->newBySku($session, 'classical-records-1');
- my $recordCollateral = $record->getAllCollateral('variantsJSON');
+ $record = WebGUI::Asset::Sku->newBySku($session, 'classical-records-1');
+ $recordCollateral = $record->getAllCollateral('variantsJSON');
cmp_deeply(
$recordCollateral,
[