diff --git a/lib/WebGUI/Commerce/Shipping.pm b/lib/WebGUI/Commerce/Shipping.pm
new file mode 100644
index 000000000..91b6e2169
--- /dev/null
+++ b/lib/WebGUI/Commerce/Shipping.pm
@@ -0,0 +1,266 @@
+package WebGUI::Commerce::Shipping;
+
+use strict;
+use WebGUI::SQL;
+use WebGUI::HTMLForm;
+use WebGUI::Commerce::ShoppingCart;
+
+
+sub calc {
+ return WebGUI::ErrorHanlder::fatal('The calc method must be overriden.');
+};
+
+sub description {
+ return $_[0]->name;
+}
+
+#-------------------------------------------------------------------
+
+=head2 configurationForm
+
+This generates the configuration form that's displayed in the admin console. You must
+extend this method to include parameters specific to this payment module. To do so return
+the SUPER::configurationForm method with a printRowsOnly'ed WebGUI::HTMLForm as the argument.
+
+Also be sure to prepend all formfield names with the prepend method. See propend for more info.
+
+=cut
+
+sub configurationForm {
+ my ($self, $form, $f);
+ $self = shift;
+ $form = shift;
+
+ $f = WebGUI::HTMLForm->new;
+ $f->yesNo(
+ -name => $self->prepend('enabled'),
+ -value => $self->enabled,
+ -label => WebGUI::International::get('enable', 'Commerce'),
+ );
+ $f->raw($form);
+
+ return $f->printRowsOnly;
+}
+
+#-------------------------------------------------------------------
+
+=head2 enabled
+
+Returns a boolean indicating whether the plugin is enabled or not.
+
+=cut
+
+sub enabled {
+ return $_[0]->{_enabled};
+}
+
+#-------------------------------------------------------------------
+
+=head2 get ( property )
+
+Returns property of the plugin.
+
+=head3 property
+
+The name of the property you want.
+
+=cut
+
+sub get {
+ return $_[0]->{_properties}{$_[1]};
+}
+
+sub getOptions {
+ return {};
+}
+
+sub getShippingItems {
+ my ($normal, $recurring, @allItems, @items, $self);
+ $self = shift;
+
+ @allItems = @{$self->{_shippingItems}};
+ unless (@allItems) {
+ ($normal, $recurring) = $self->getShoppingCart->getItems;
+ @allItems = (@$normal, @$recurring);
+ }
+ foreach (@allItems) {
+ push(@items, $_) if $_->{item}->needsShipping;
+ }
+
+ return [ @items ];
+}
+
+#-------------------------------------------------------------------
+
+=head2 getEnabledPlugins
+
+Returns a reference to an array of all enabled instanciated payment plugins.
+
+=cut
+
+sub getEnabledPlugins {
+ my (@enabledPlugins, $plugin, @plugins);
+ @enabledPlugins = WebGUI::SQL->buildArray("select namespace from commerceSettings where type='Shipping' and fieldName='enabled' and fieldValue='1'");
+
+ foreach (@enabledPlugins) {
+ $plugin = WebGUI::Commerce::Shipping->load($_);
+ push(@plugins, $plugin) if ($plugin);
+ }
+
+ return \@plugins;
+}
+
+#-------------------------------------------------------------------
+
+=head2 init ( namespace )
+
+Constructor for the plugin. You should extend this method.
+
+=head3 namespace
+
+The namespace of the plugin.
+
+=cut
+
+sub init {
+ my ($class, $namespace, $properties, $shoppingCart);
+ $class = shift;
+ $namespace = shift;
+
+ WebGUI::ErrorHandler::fatal('No namespace passed to init.') unless ($namespace);
+
+ $properties = WebGUI::SQL->buildHashRef("select fieldName, fieldValue from commerceSettings where namespace=".quote($namespace)." and type='Shipping'");
+ $shoppingCart = WebGUI::Commerce::ShoppingCart->new;
+
+ bless {_properties=>$properties,
+ _shippingParameters => {},
+ _shoppingCart => $shoppingCart,
+ _namespace=>$namespace,
+ _enabled=>$properties->{enabled},
+ _shippingItems => []}, $class;
+}
+
+#-------------------------------------------------------------------
+
+sub getShoppingCart {
+ return $_[0]->{_shoppingCart};
+};
+
+#-------------------------------------------------------------------
+
+=head2 load ( namespace )
+
+A convienient method to load a plugin. It handles all error checking and stuff for you.
+This is a SUPER class method only and shoud NOT be overridden.
+
+=head3 namespace
+
+The namespace of the plugin.
+
+=cut
+
+sub load {
+ my ($class, $namespace, $load, $cmd, $plugin);
+ $class = shift;
+ $namespace = shift;
+
+ WebGUI::ErrorHandler::fatal('No namespace passed to load.') unless ($namespace);
+
+ $cmd = "WebGUI::Commerce::Shipping::$namespace";
+ $load = "use $cmd";
+ eval($load);
+ WebGUI::ErrorHandler::warn("Shipping plugin failed to compile: $cmd.".$@) if($@);
+ $plugin = eval($cmd."->init");
+ WebGUI::ErrorHandler::warn("Couldn't instantiate shipping plugin: $cmd.".$@) if($@);
+ return $plugin;
+}
+
+#-------------------------------------------------------------------
+
+=head2 name
+
+Returns the (display) name of the plugin. You must override this method.
+
+=cut
+
+sub name {
+ return WebGUI::ErrorHandler::fatal("You must override the name method in the shipping plugin.");
+}
+
+#-------------------------------------------------------------------
+
+=head2 namespace
+
+Returns the namespace of the plugin.
+
+=cut
+
+sub namespace {
+ return $_[0]->{_namespace};
+}
+
+sub optionsOk {
+ return 1;
+}
+
+#-------------------------------------------------------------------
+
+=head2 prepend ( fieldName )
+
+A utility method that prepends fieldName with a string that's used to save configuration data to
+the database. Use it on all fields in the configurationForm method.
+
+For instance:
+
+ $f = WebGUI::HTMLForm->new;
+ $f->text(
+ -name => $self->prepend('MyField');
+ -label => 'MyField'
+ );
+
+=head3 fieldName
+
+The string to prepend.
+
+=cut
+
+sub prepend {
+ my ($self, $name);
+ $self = shift;
+ $name = shift;
+
+ return "~Shipping~".$self->namespace."~".$name;
+}
+
+sub processOptionsForm {
+}
+
+sub setOptions {
+}
+
+sub setShippingItems {
+ my ($self, $items);
+ $self = shift;
+ $items = shift;
+
+ $self->{_shippingItems} = $items;
+}
+
+sub supportsTracking {
+ return 0;
+}
+
+sub trackingInfo {
+ return {};
+}
+
+sub trackingNumber {
+ return undef;
+}
+
+sub optionsOk {
+ return 1;
+};
+
+1;
+
diff --git a/lib/WebGUI/i18n/English/CommerceShippingByPrice.pm b/lib/WebGUI/i18n/English/CommerceShippingByPrice.pm
new file mode 100644
index 000000000..25afe5b4d
--- /dev/null
+++ b/lib/WebGUI/i18n/English/CommerceShippingByPrice.pm
@@ -0,0 +1,18 @@
+package WebGUI::i18n::English::CommerceShippingByPrice;
+
+our $I18N = {
+ 'percentage of price' => {
+ message => q|Percentage of price|,
+ lastUpdated => 0,
+ context => q|The label of the percentage of price form element.|
+ },
+ 'title' => {
+ message => q|By Price|,
+ lastUpdated => 0,
+ context => q|The title of the by price module.|
+ },
+
+};
+
+1;
+
diff --git a/lib/WebGUI/i18n/English/CommerceShippingByWeight.pm b/lib/WebGUI/i18n/English/CommerceShippingByWeight.pm
new file mode 100644
index 000000000..c5d4f0ea2
--- /dev/null
+++ b/lib/WebGUI/i18n/English/CommerceShippingByWeight.pm
@@ -0,0 +1,18 @@
+package WebGUI::i18n::English::CommerceShippingByWeight;
+
+our $I18N = {
+ 'title' => {
+ message => q|By Weight|,
+ lastUpdated => 0,
+ context => q|The title of the by weight shipping plugin.|
+ },
+ 'price per weight' => {
+ message => q|Price per weight|,
+ lastUpdated => 0,
+ context => q|The label of the price per weight form element.|
+ },
+
+};
+
+1;
+
diff --git a/lib/WebGUI/i18n/English/CommerceShippingPerTransaction.pm b/lib/WebGUI/i18n/English/CommerceShippingPerTransaction.pm
new file mode 100644
index 000000000..7770304d9
--- /dev/null
+++ b/lib/WebGUI/i18n/English/CommerceShippingPerTransaction.pm
@@ -0,0 +1,18 @@
+package WebGUI::i18n::English::CommerceShippingPerTransaction;
+
+our $I18N = {
+ 'title' => {
+ message => q|Per transaction|,
+ lastUpdated => 0,
+ context => q|The title of the pertransaction plugin.|
+ },
+ 'price' => {
+ message => q|Price|,
+ lastUpdated => 0,
+ context => q|The label of the price form element.|
+ },
+
+};
+
+1;
+
diff --git a/lib/WebGUI/i18n/English/Macro_Product.pm b/lib/WebGUI/i18n/English/Macro_Product.pm
new file mode 100644
index 000000000..4df043d2a
--- /dev/null
+++ b/lib/WebGUI/i18n/English/Macro_Product.pm
@@ -0,0 +1,17 @@
+package WebGUI::i18n::English::Macro_Product;
+
+our $I18N = {
+ 'add to cart' => {
+ message => q|Add to cart|,
+ lastUpdated => 0,
+ context => q|The label for the add to cart link.|
+ },
+ 'available product configurations' => {
+ message => q|Available product configurations|,
+ lastUpdated => 0,
+ context => q|Message indicatin the available configurations.|
+ }
+};
+
+1;
+
diff --git a/lib/WebGUI/i18n/English/ProductManager.pm b/lib/WebGUI/i18n/English/ProductManager.pm
new file mode 100644
index 000000000..ef3d4d541
--- /dev/null
+++ b/lib/WebGUI/i18n/English/ProductManager.pm
@@ -0,0 +1,579 @@
+package WebGUI::i18n::English::ProductManager;
+
+our $I18N = {
+ 'manage products' => {
+ message => q|Products|,
+ lastUpdated => 0,
+ context => q|The admin console label of the product manager.|
+ },
+ 'title' => {
+ message => q|Title|,
+ lastUpdated => 1101772584,
+ context => q|The form label for the title field in editProduct|
+ },
+ 'description' => {
+ message => q|Description|,
+ lastUpdated => 1101772584,
+ context => q|The form label for the description field in editProduct|
+ },
+ 'price' => {
+ message => q|Price|,
+ lastUpdated => 1101772584,
+ context => q|The form label for the price field in editProduct|
+ },
+ 'weight' => {
+ message => q|Weight|,
+ lastUpdated => 1101772584,
+ context => q|The form label for the weight field in editProduct|
+ },
+ 'sku' => {
+ message => q|SKU|,
+ lastUpdated => 1101772584,
+ context => q|The form label for the SKU (Stock Keeping Unit) field in editProduct|
+ },
+ 'template' => {
+ message => q|Template|,
+ lastUpdated => 1101772584,
+ context => q|The form label for the template field in editProduct|
+ },
+ 'edit product' => {
+ message => q|Edit product|,
+ lastUpdated => 1101772584,
+ context => q|The name of the edit product form|
+ },
+
+ 'edit product title error' => {
+ message => q|You must enter a product title.|,
+ lastUpdated => 1101772584,
+ context => q|An errormessage when no product title is given.|
+ },
+ 'edit product price error' => {
+ message => q|You must enter a price.|,
+ lastUpdated => 1101772584,
+ context => q|An errormessage when no product price is given.|
+ },
+ 'edit product weight error' => {
+ message => q|You must enter a weight.|,
+ lastUpdated => 1101772584,
+ context => q|An errormessage when no product weight is given.|
+ },
+ 'edit product sku error' => {
+ message => q|You must enter a SKU|,
+ lastUpdated => 1101772584,
+ context => q|An errormessage when no product SKU is given.|
+ },
+ 'edit parameter name' => {
+ message => q|Name|,
+ lastUpdated => 0000,
+ context => q|The form label for the name field in editProductParameter|
+ },
+ 'edit parameter' => {
+ message => q|Edit product parameter|,
+ lastUpdated => 0000,
+ context => q|The name of the editProductParameter form|
+ },
+ 'edit parameter name error' => {
+ message => q|You must enter a parameter name.|,
+ lastUpdated => 0000,
+ context => q|An errormessage when no parameter name is given.|
+ },
+ 'edit parameter productId error' => {
+ message => q|No product ID supplied.|,
+ lastUpdated => 0000,
+ context => q|An errormessage when no productId for a parameter is given.|
+ },
+ 'edit option value' => {
+ message => q|Value|,
+ lastUpdated => 0000,
+ context => q|The form label for the value field in editProductParameterOption|
+ },
+ 'edit option price modifier' => {
+ message => q|Price modifier|,
+ lastUpdated => 0000,
+ context => q|The form label for the priceModifier field in editProductParameterOption|
+ },
+ 'edit option weight modifier' => {
+ message => q|Weight modifier|,
+ lastUpdated => 0000,
+ context => q|The form label for the weightModifier field in editProductParameterOption|
+ },
+ 'edit option sku modifier' => {
+ message => q|SKU modifier|,
+ lastUpdated => 0000,
+ context => q|The form label for the skuModifier field in editProductParameterOption|
+ },
+ 'edit option' => {
+ mesaage => q|Edit parameter option|,
+ lastUpdated => 0000,
+ context => q|The name of de edit parameter option form|
+ },
+ 'edit option value error' => {
+ message => q|You must enter a value.|,
+ lastupdated => 0000,
+ context => q|An errormessage when no value for an option is given.|
+ },
+ 'edit option parameterId error' => {
+ message => q|No parameter ID given.|,
+ lastUpdated => 0000,
+ context => q|An errormessage when no parameterId for a parameter is given.|
+ },
+ 'list products' => {
+ message => q|List products|,
+ lastUpdated => 0000,
+ context => q|The title of the list product screen|
+ },
+ 'add product' => {
+ message => q|Add a new product|,
+ lastUpdated => 0,
+ context => q|The label for the add product link in de productmanager menu.|
+ },
+ 'list products' => {
+ message => q|List products|,
+ lastUpdated => 0,
+ context => q|The label for the list products link in de productmanager menu.|
+ },
+ 'manage product' => {
+ message => q|Manage product|,
+ lastUpdated => 0,
+ context => q|The label for the manage product link in de productmanager menu.|
+ },
+ 'list variants' => {
+ message => q|List variants|,
+ lastUpdated => q|List variants|,
+ context => q|The label for the list variants in de productmanager menu.|
+ },
+ 'sku template' => {
+ message => q|SKU Template|,
+ lastUpdated => 0,
+ context => q|The label for the sku template field in the edit product screen.|
+ },
+ 'edit sku composition label' => {
+ message => q|Edit SKU Composition|,
+ lastUpdated => 0,
+ context => q|The label of the edit sku composition|
+ },
+ 'list variants label' => {
+ message => q|List product variants|,
+ lastUpdated => 0,
+ context => q|The label of the list variants screen.|
+ },
+ 'available' => {
+ message => q|Available|,
+ lastUpdated => 0,
+ context => q|A message indicating that a variant is available.|
+ },
+ 'properties' => {
+ message => q|Properties|,
+ lastUpdated => 0,
+ context => q|Properties|
+ },
+ 'add parameter' => {
+ message => q|Add parameter|,
+ lastUpdated => 0,
+ context => q|The label of the add parameter link in manage product.|
+ },
+ 'add option' => {
+ message => q|Add option|,
+ lastUpdated => 0,
+ context => q|The label of the add option link in manage product.|
+ },
+ 'manage product label' => {
+ message => q|Manage product|,
+ lastUpdated => 0,
+ context => q|The label of the manage product screen.|
+ },
+ 'price override' => {
+ message => q|Price override|,
+ lastUpdated => 0,
+ context => q|Form label in the edit variant screen.|
+ },
+ 'weight override' => {
+ message => q|Weight override|,
+ lastUpdated => 0,
+ context => q|Form label in the edit variant screen.|
+ },
+ 'sku override' => {
+ message => q|SKU override|,
+ lastUpdated => 0,
+ context => q|Form label in the edit variant screen.|
+ },
+ 'edit variant' => {
+ message => q|Edit product variant|,
+ lastUpdated => 0,
+ context => q|Label of the edit variant screen.|
+ },
+
+ 'help list products title' => {
+ message => q|List products|,
+ lastUpdated => 0,
+ context => q|The title of the list products help page|
+ },
+ 'help list products body' => {
+ message => q|Overview
+
+Next to subscriptions WebGUI features products. Product allow you to
+sell your products with considerable ease. Just define a base product
+and add parameters to it if you need to. WebGUI generates all possible
+product configuration for you and sets the price, weight and sku values
+according to your wishes automatically.
+
+For example you want to sell T-shirts in the sizes S, M, L and XL and
+the colors blue and grey. Also an XL costs $ 1.50 extra.
+
+To do this just add a product called T-shirt and add parameters color
+and size to it. Add to the color parameter the options 'blue' and
+'grey', and add 'S', 'M', 'L' and 'XL' to the size parameter. Set the
+price modifier of the 'XL' option to 1.50.
+
+Now you have created eight variants of your T-shirt product and only in
+about a minute.
+
+If you are out of blue large T-shirts, just disable this variant. You
+are in full controll.
+
+Usage
+
+You can add new products with the 'Add a new product' link in the
+righthand menu. To manage an existing product click on the edit button
+left of it. Use the delete button to remove it.
+
|,
+ lastUpdated => 0,
+ context => q|The body of the list products help page|
+ },
+
+ 'help manage product title' => {
+ message => q|Manage Product|,
+ lastUpdated => 0,
+ context => q|The title of the manage product help page|
+ },
+ 'help manage product body' => {
+ message => q|The manage product menu allows you to inspect and edit the properties
+of your product.
+
+Overview
+
+Products consist of their properties and parameters. The properties are
+the base of your product. They define the the default price, weight,
+sku and sku composition of the product and all it's variants.
+
+Parameters are the different forms in which your product comes. For
+instance if you're selling a T-shirt, 'Size' and 'Color' are parameters.
+
+Parameters consist of options. An option is a value a parameter can
+take, such as 'Blue', 'Grey' or 'Rainbow'. Options also allow you to
+set modifiers for price, weight and sku. These modifiers add to the the
+base price, weight and sku defined in the properties of your product. A
+price modifier of 5.00 for 'Rainbow' increases the price with
+$5.00 for rainbow-colored shirts.
+
+Properties
+
+The properties section shows the base values for your product. Every
+product configuration you sell is based on these values. Modifiers set
+in options are relative to these values. The properties of a product
+are:
+
+