]> git.mxchange.org Git - jfinancials-war.git/commitdiff
Maybe cherry-pick:
authorRoland Häder <roland@mxchange.org>
Tue, 31 Oct 2017 18:44:33 +0000 (19:44 +0100)
committerRoland Häder <roland@mxchange.org>
Tue, 31 Oct 2017 18:55:33 +0000 (19:55 +0100)
- added mini-links JSF tags for product categories and generic products
- added form templates for above again
- added views for listing both again
- added navigation rules to faces-config.xml
- rewrote converter to CDI "lookup" (programatic) which finally let the
  converter work without any EJB calls
- added filtered list for categories and products
- added product unit amount and type, like 1 liter
- categories and products have now both i18n keys and not direct and
  untranslateable titles
- rewrote + sorted renderGenericProduct() as products now have i18n keys
- added menu entries

Signed-off-by: Roland Häder <roland@mxchange.org>
17 files changed:
src/java/org/mxchange/jfinancials/beans/category/FinancialAdminCategoryWebRequestBean.java
src/java/org/mxchange/jfinancials/beans/category/FinancialCategoryWebRequestBean.java
src/java/org/mxchange/jfinancials/beans/helper/FinancialsWebRequestHelperBean.java
src/java/org/mxchange/jfinancials/beans/product/FinancialAdminProductWebRequestBean.java
src/java/org/mxchange/jfinancials/beans/product/FinancialProductWebRequestBean.java
src/java/org/mxchange/jfinancials/converter/generic_product/FinancialsGenericProductConverter.java
src/java/org/mxchange/jfinancials/converter/product_category/FinancialsProductCategoryConverter.java
web/WEB-INF/faces-config.xml
web/WEB-INF/product-links.jsf.taglib.xml [new file with mode: 0644]
web/WEB-INF/resources/tags/admin/links/mini/category/admin_product_category_links.tpl [new file with mode: 0644]
web/WEB-INF/resources/tags/admin/links/mini/product/admin_generic_product_links.tpl [new file with mode: 0644]
web/WEB-INF/templates/admin/category/admin_form_category_data.tpl [new file with mode: 0644]
web/WEB-INF/templates/admin/menu/project.tpl
web/WEB-INF/templates/admin/product/admin_form_product_data.tpl [new file with mode: 0644]
web/WEB-INF/web.xml
web/admin/category/admin_product_category_list.xhtml [new file with mode: 0644]
web/admin/product/admin_generic_product_list.xhtml [new file with mode: 0644]

index 3a746f2c58e18913d3248565ccbfc7759592974c..1764c64fab0d08df8bfc2666177619d6e70a5bfd 100644 (file)
@@ -64,9 +64,9 @@ public class FinancialAdminCategoryWebRequestBean extends BaseFinancialsBean imp
        private Boolean categoryShownInStatistics;
 
        /**
-        * Category categoryTitle
+        * Category categoryI18nKey
         */
-       private String categoryTitle;
+       private String categoryI18nKey;
 
        /**
         * Parent category
@@ -128,21 +128,21 @@ public class FinancialAdminCategoryWebRequestBean extends BaseFinancialsBean imp
        }
 
        /**
-        * Getter for category title
+        * Getter for category i18n key
         * <p>
-        * @return the title
+        * @return Category i18n key
         */
-       public String getCategoryTitle () {
-               return this.categoryTitle;
+       public String getCategoryI18nKey () {
+               return this.categoryI18nKey;
        }
 
        /**
-        * Setter for category title
+        * Setter for category i18n key
         * <p>
-        * @param categoryTitle the title to set
+        * @param categoryI18nKey Category i18n key
         */
-       public void setCategoryTitle (final String categoryTitle) {
-               this.categoryTitle = categoryTitle;
+       public void setCategoryI18nKey (final String categoryI18nKey) {
+               this.categoryI18nKey = categoryI18nKey;
        }
 
        /**
@@ -168,7 +168,7 @@ public class FinancialAdminCategoryWebRequestBean extends BaseFinancialsBean imp
         */
        private void clear () {
                // Clear all fields
-               this.setCategoryTitle(null);
+               this.setCategoryI18nKey(null);
                this.setParentCategory(null);
        }
 
@@ -179,7 +179,7 @@ public class FinancialAdminCategoryWebRequestBean extends BaseFinancialsBean imp
         */
        private Category createCategoryInstance () {
                // Create category
-               final Category category = new ProductCategory(this.getCategoryTitle(), this.getParentCategory(), this.getCategoryShownInStatistics());
+               final Category category = new ProductCategory(this.getCategoryI18nKey(), this.getParentCategory(), this.getCategoryShownInStatistics());
 
                // Return it
                return category;
index d9d588c9c5f9ac9ca9916c07b3497bcd0e12db24..e45879a9fc6793b36472fbd118a829a7fcaec233 100644 (file)
@@ -52,7 +52,7 @@ public class FinancialCategoryWebRequestBean extends BaseFinancialsBean implemen
        /**
         * List of all categories
         */
-       private List<Category> allCategories;
+       private final List<Category> allCategories;
 
        /**
         * EJB for general category stuff
@@ -67,6 +67,11 @@ public class FinancialCategoryWebRequestBean extends BaseFinancialsBean implemen
        @NamedCache (cacheName = "categoryCache")
        private Cache<Long, Category> categoryCache;
 
+       /**
+        * A list of filtered categories
+        */
+       private List<Category> filteredCategories;
+
        /**
         * Default constructor
         */
@@ -135,6 +140,26 @@ public class FinancialCategoryWebRequestBean extends BaseFinancialsBean implemen
                return category;
        }
 
+       /**
+        * Getter for filtered category list
+        * <p>
+        * @return Filtered category list
+        */
+       @SuppressWarnings ("ReturnOfCollectionOrArrayField")
+       public List<Category> getFilteredCategories () {
+               return this.filteredCategories;
+       }
+
+       /**
+        * Setter for filtered category list
+        * <p>
+        * @param filteredCategories Filtered category list
+        */
+       @SuppressWarnings ("AssignmentToCollectionOrArrayFieldFromParameter")
+       public void setFilteredCategories (final List<Category> filteredCategories) {
+               this.filteredCategories = filteredCategories;
+       }
+
        /**
         * Initialization of this bean
         */
index e07d84d97f554e59d613693c1c4b02d28f3b5a28..5592f36991559c90f62672e5fa1f4c8735d4a4b3 100644 (file)
@@ -42,6 +42,7 @@ import org.mxchange.jphone.events.mobile.created.ObservableCreatedMobileNumberEv
 import org.mxchange.jphone.model.phonenumbers.fax.DialableFaxNumber;
 import org.mxchange.jphone.model.phonenumbers.landline.DialableLandLineNumber;
 import org.mxchange.jphone.model.phonenumbers.mobile.DialableMobileNumber;
+import org.mxchange.jproduct.model.category.Category;
 import org.mxchange.jproduct.model.product.Product;
 import org.mxchange.jusercore.events.user.created.CreatedUserEvent;
 import org.mxchange.jusercore.events.user.created.ObservableCreatedUserEvent;
@@ -566,6 +567,31 @@ public class FinancialsWebRequestHelperBean extends BaseFinancialsBean implement
                return sb.toString();
        }
 
+       /**
+        * Returns the product name and price. If null is provided, an empty string
+        * is returned.
+        * <p>
+        * @param product Product instance
+        * <p>
+        * @return Product name
+        */
+       public String renderGenericProduct (final Product product) {
+               // Default is empty string, so let's get started
+               final StringBuilder sb = new StringBuilder(10);
+
+               // Is a product set?
+               if (product instanceof Product) {
+                       // Add name and price
+                       sb.append(product.getProductI18nKey());
+                       sb.append(" ("); //NOI18N
+                       sb.append(this.localizationController.formatCurrency(product.getProductGrossPrice()));
+                       sb.append(")"); //NOI18N
+               }
+
+               // Return it
+               return sb.toString();
+       }
+
        /**
         * Returns the headquarters address. If null is provided, an empty string is
         * returned.
@@ -612,24 +638,29 @@ public class FinancialsWebRequestHelperBean extends BaseFinancialsBean implement
        }
 
        /**
-        * Returns the product name and price. If null is provided, an empty string
-        * is returned.
+        * Returns the category's i18n string translated. If null is provided, an
+        * empty string is returned.
         * <p>
-        * @param product Product instance
+        * @param category Product category instance
         * <p>
-        * @return Product name
+        * @return Category's i18n string translation
         */
-       public String renderProduct (final Product product) {
+       public String renderProductCategory (final Category category) {
                // Default is empty string, so let's get started
                final StringBuilder sb = new StringBuilder(10);
 
-               // Is a product set?
-               if (product instanceof Product) {
-                       // Add name and price
-                       sb.append(product.getProductTitle());
-                       sb.append(" ("); //NOI18N
-                       sb.append(this.localizationController.formatCurrency(product.getProductGrossPrice()));
-                       sb.append(")"); //NOI18N
+               // Is a category set?
+               if (category instanceof Category) {
+                       // Add title
+                       sb.append(this.getMessageFromBundle(category.getCategoryI18nKey()));
+
+                       // Is a parent category set?
+                       if (category.getParentCategory() instanceof Category) {
+                               // Then add it in braces, too
+                               sb.append(" ("); //NOI18N
+                               sb.append(this.getMessageFromBundle(category.getParentCategory().getCategoryI18nKey()));
+                               sb.append(")"); //NOI18N
+                       }
                }
 
                // Return it
index db8058be225c64585c419d5883a373fb2baf474c..842d3b1fe4d0473cb8f79860e66fd34d20028df3 100644 (file)
@@ -81,8 +81,8 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
        /**
         * Remote bean for products
         */
-       @EJB (lookup = "java:global/jfinancial-ejb/adminProduct!org.mxchange.jproduct.model.product.AdminProductSessionBeanRemote")
-       private AdminProductSessionBeanRemote productRemoteBean;
+       @EJB (lookup = "java:global/jfinancials-ejb/adminProduct!org.mxchange.jproduct.model.product.AdminProductSessionBeanRemote")
+       private AdminProductSessionBeanRemote adminProductBean;
 
        /**
         * Product's tax rate
@@ -90,9 +90,19 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
        private Float productTaxRate;
 
        /**
-        * Property productTitle
+        * I18n key of product
         */
-       private String productTitle;
+       private String productI18nKey;
+
+       /**
+        * Product's unit amount
+        */
+       private Float productUnitAmount;
+
+       /**
+        * Product's unit type
+        */
+       private String productUnitType;
 
        /**
         * Default constructor
@@ -116,7 +126,7 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
 
                try {
                        // Call bean
-                       updatedProduct = this.productRemoteBean.addGenericProduct(product);
+                       updatedProduct = this.adminProductBean.addGenericProduct(product);
                } catch (final ProductAlreadyAddedException ex) {
                        // Continue to throw
                        throw new FaceletException(ex);
@@ -130,18 +140,18 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
        }
 
        /**
-        * Getter for product's available property
+        * Getter for product's available
         * <p>
-        * @return Product's available property
+        * @return Product's available
         */
        public Boolean getProductAvailability () {
                return this.productAvailability;
        }
 
        /**
-        * Setter for product's available property
+        * Setter for product's available
         * <p>
-        * @param productAvailability Product's available property
+        * @param productAvailability Product's available
         */
        public void setProductAvailability (final Boolean productAvailability) {
                this.productAvailability = productAvailability;
@@ -238,21 +248,57 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
        }
 
        /**
-        * Getter for product's title property
+        * Getter for product's i18n key
+        * <p>
+        * @return Product's i18n key
+        */
+       public String getProductI18nKey () {
+               return this.productI18nKey;
+       }
+
+       /**
+        * Setter for product's i18n key
+        * <p>
+        * @param productI18nKey Product's i18n key
+        */
+       public void setProductI18nKey (final String productI18nKey) {
+               this.productI18nKey = productI18nKey;
+       }
+
+       /**
+        * Getter for product's unit amount
+        * <p>
+        * @return Product's unit amount
+        */
+       public Float getProductUnitAmount () {
+               return this.productUnitAmount;
+       }
+
+       /**
+        * Setter for product's unit amount
+        * <p>
+        * @param productUnitAmount Product's unit amount
+        */
+       public void setProductUnitAmount (final Float productUnitAmount) {
+               this.productUnitAmount = productUnitAmount;
+       }
+
+       /**
+        * Getter for product's unit type
         * <p>
-        * @return Product's title
+        * @return Product's unit type
         */
-       public String getProductTitle () {
-               return this.productTitle;
+       public String getProductUnitType () {
+               return this.productUnitType;
        }
 
        /**
-        * Setter for product's title property
+        * Setter for product's unit type
         * <p>
-        * @param productTitle Product's title
+        * @param productUnitType Product's unit type
         */
-       public void setProductTitle (final String productTitle) {
-               this.productTitle = productTitle;
+       public void setProductUnitType (final String productUnitType) {
+               this.productUnitType = productUnitType;
        }
 
        /**
@@ -264,7 +310,9 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
                this.setProductNetPrice(null);
                this.setProductTaxRate(null);
                this.setProductGrossPrice(null);
-               this.setProductTitle(null);
+               this.setProductI18nKey(null);
+               this.setProductUnitAmount(null);
+               this.setProductUnitType(null);
        }
 
        /**
@@ -274,11 +322,13 @@ public class FinancialAdminProductWebRequestBean extends BaseFinancialsBean impl
         */
        private Product createProductInstance () {
                // Create product instance
-               final Product product = new GenericProduct(this.getProductTitle(), this.getProductGrossPrice(), this.getProductCurrencyCode(), this.getProductCategory(), this.getProductAvailability());
+               final Product product = new GenericProduct(this.getProductI18nKey(), this.getProductGrossPrice(), this.getProductCurrencyCode(), this.getProductCategory(), this.getProductAvailability());
 
                // Set all optional fields
                product.setProductNetPrice(this.getProductNetPrice());
                product.setProductTaxRate(this.getProductTaxRate());
+               product.setProductUnitAmount(this.getProductUnitAmount());
+               product.setProductUnitType(this.getProductUnitType());
 
                // Return it
                return product;
index 88da637e434c9c613098711f362d7d434980c728..635cbe827df92996a3b6b9a02c9f54cf165bcf2d 100644 (file)
@@ -51,10 +51,15 @@ public class FinancialProductWebRequestBean extends BaseFinancialsBean implement
        private static final long serialVersionUID = 58_137_539_530_279L;
 
        /**
-        * List for all available products
+        * List for all products
         */
        private List<Product> allProducts;
 
+       /**
+        * List for filtered products
+        */
+       private List<Product> filteredProducts;
+
        /**
         * EJB for general product purposes
         */
@@ -137,6 +142,26 @@ public class FinancialProductWebRequestBean extends BaseFinancialsBean implement
                return product;
        }
 
+       /**
+        * Getter for filtered product list
+        * <p>
+        * @return Filtered product list
+        */
+       @SuppressWarnings ("ReturnOfCollectionOrArrayField")
+       public List<Product> getFilteredProducts () {
+               return this.filteredProducts;
+       }
+
+       /**
+        * Setter for filtered product list
+        * <p>
+        * @param filteredProducts Filtered product list
+        */
+       @SuppressWarnings ("AssignmentToCollectionOrArrayFieldFromParameter")
+       public void setFilteredProducts (final List<Product> filteredProducts) {
+               this.filteredProducts = filteredProducts;
+       }
+
        /**
         * Initialization of this bean
         */
index 7d03212dd43333f5768a3ab6d4596690c9423e1d..5af8d5fd0808c73ed5fa228d7b9a5bea4f7043c9 100644 (file)
  */
 package org.mxchange.jfinancials.converter.generic_product;
 
-import javax.faces.application.FacesMessage;
+import javax.enterprise.inject.spi.CDI;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import javax.faces.convert.Converter;
 import javax.faces.convert.ConverterException;
 import javax.faces.convert.FacesConverter;
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
+import org.mxchange.jfinancials.beans.product.FinancialProductWebRequestBean;
 import org.mxchange.jfinancials.beans.product.FinancialProductWebRequestController;
 import org.mxchange.jproduct.exceptions.product.ProductNotFoundException;
 import org.mxchange.jproduct.model.product.Product;
@@ -46,16 +44,8 @@ public class FinancialsGenericProductConverter implements Converter<Product> {
        public Product getAsObject (final FacesContext context, final UIComponent component, final String submittedValue) {
                // Is the instance there?
                if (PRODUCT_CONTROLLER == null) {
-                       try {
-                               // Not yet, attempt lookup
-                               final Context initial = new InitialContext();
-
-                               // Lookup EJB
-                               PRODUCT_CONTROLLER = (FinancialProductWebRequestController) initial.lookup("java:global/jfinancials-ejb/productController!org.mxchange.jfinancials.beans.product.FinancialProductWebRequestController");
-                       } catch (final NamingException ex) {
-                               // Throw it again
-                               throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Cannot lookup backing bean", ex.getMessage()), ex);
-                       }
+                       // Get bean from CDI directly
+                       PRODUCT_CONTROLLER = CDI.current().select(FinancialProductWebRequestBean.class).get();
                }
 
                // Is the value null or empty?
index a1753d0b1f61256c58b74fcd8ee4a025b1c4422e..983e8b3f4a3da15e5266d2be96dc259cb9da0fa5 100644 (file)
  */
 package org.mxchange.jfinancials.converter.product_category;
 
-import javax.faces.application.FacesMessage;
+import javax.enterprise.inject.spi.CDI;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import javax.faces.convert.Converter;
 import javax.faces.convert.ConverterException;
 import javax.faces.convert.FacesConverter;
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
 import org.mxchange.jfinancials.beans.category.FinancialCategoryWebRequestBean;
 import org.mxchange.jfinancials.beans.category.FinancialCategoryWebRequestController;
 import org.mxchange.jproduct.exceptions.category.CategoryNotFoundException;
@@ -47,16 +44,8 @@ public class FinancialsProductCategoryConverter implements Converter<Category> {
        public Category getAsObject (final FacesContext context, final UIComponent component, final String submittedValue) {
                // Is the instance there?
                if (CATEGORY_CONTROLLER == null) {
-                       try {
-                               // Not yet, attempt lookup
-                               final Context initial = new InitialContext();
-
-                               // Lookup EJB
-                               CATEGORY_CONTROLLER = (FinancialCategoryWebRequestController) initial.lookup(String.format("java:module/%s", FinancialCategoryWebRequestBean.class.getSimpleName()));
-                       } catch (final NamingException ex) {
-                               // Throw it again
-                               throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Cannot lookup backing bean", ex.getMessage()), ex);
-                       }
+                       // Get bean from CDI directly
+                       CATEGORY_CONTROLLER = CDI.current().select(FinancialCategoryWebRequestBean.class).get();
                }
 
                // Is the value null or empty?
index cab11327cdee1243b5cc290b3a2ab631feb9d5c3..b6471e276e24a5c99d54dd03676aa0f06cc5e08b 100644 (file)
                        <from-outcome>admin_list_company_employee</from-outcome>
                        <to-view-id>/admin/employee/admin_employee_list.xhtml</to-view-id>
                </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_list_category</from-outcome>
+                       <to-view-id>/admin/category/admin_product_category_list.xhtml</to-view-id>
+               </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_list_product</from-outcome>
+                       <to-view-id>/admin/product/admin_generic_product_list.xhtml</to-view-id>
+               </navigation-case>
                <navigation-case>
                        <from-outcome>admin_list_user</from-outcome>
                        <to-view-id>/admin/user/admin_user_list.xhtml</to-view-id>
                        <to-view-id>/admin/opening_time/admin_opening_time_delete.xhtml</to-view-id>
                </navigation-case>
        </navigation-rule>
+       <navigation-rule>
+               <from-view-id>/admin/category/admin_product_category_list.xhtml</from-view-id>
+               <navigation-case>
+                       <from-outcome>admin_show_product_category</from-outcome>
+                       <to-view-id>/admin/category/admin_product_category_show.xhtml</to-view-id>
+               </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_edit_product_category</from-outcome>
+                       <to-view-id>/admin/category/admin_product_category_edit.xhtml</to-view-id>
+               </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_delete_product_category</from-outcome>
+                       <to-view-id>/admin/category/admin_product_category_delete.xhtml</to-view-id>
+               </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_assign_parent_category</from-outcome>
+                       <to-view-id>/admin/category/admin_product_category_assign_parent.xhtml</to-view-id>
+               </navigation-case>
+       </navigation-rule>
+       <navigation-rule>
+               <from-view-id>/admin/product/admin_generic_product_list.xhtml</from-view-id>
+               <navigation-case>
+                       <from-outcome>admin_show_generic_product</from-outcome>
+                       <to-view-id>/admin/product/admin_generic_product_show.xhtml</to-view-id>
+               </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_edit_generic_product</from-outcome>
+                       <to-view-id>/admin/product/admin_generic_product_edit.xhtml</to-view-id>
+               </navigation-case>
+               <navigation-case>
+                       <from-outcome>admin_delete_generic_product</from-outcome>
+                       <to-view-id>/admin/product/admin_generic_product_delete.xhtml</to-view-id>
+               </navigation-case>
+       </navigation-rule>
        <!--
        <factory>
                <exception-handler-factory>
diff --git a/web/WEB-INF/product-links.jsf.taglib.xml b/web/WEB-INF/product-links.jsf.taglib.xml
new file mode 100644 (file)
index 0000000..637991c
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2017 Roland Häder
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<facelet-taglib version="2.2"
+                               xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+                               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
+       <namespace>http://mxchange.org/jsf/jproduct/links</namespace>
+       <tag>
+               <tag-name>outputProductCategoryAdminMiniLinks</tag-name>
+               <description>This tag renders administrative "mini-links" for given category instance.</description>
+               <source>resources/tags/admin/links/mini/category/admin_product_category_links.tpl</source>
+               <attribute>
+                       <name>category</name>
+                       <description>The product category instance that provides the data for this tag.</description>
+                       <required>true</required>
+                       <type>org.mxchange.jproduct.model.category.Category</type>
+               </attribute>
+               <attribute>
+                       <name>renderShowLink</name>
+                       <description>Whether to render (default: true) "show product" link.</description>
+                       <required>false</required>
+                       <type>java.langBoolean</type>
+               </attribute>
+               <attribute>
+                       <name>rendered</name>
+                       <description>Whether this tag is being rendered by JSF engine (default: true).</description>
+                       <required>false</required>
+                       <type>java.lang.Boolean</type>
+               </attribute>
+       </tag>
+       <tag>
+               <tag-name>outputGenericProductAdminMiniLinks</tag-name>
+               <description>This tag renders administrative "mini-links" for given product instance.</description>
+               <source>resources/tags/admin/links/mini/product/admin_generic_product_links.tpl</source>
+               <attribute>
+                       <name>product</name>
+                       <description>The generic product instance that provides the data for this tag.</description>
+                       <required>true</required>
+                       <type>org.mxchange.jproduct.model.product.Product</type>
+               </attribute>
+               <attribute>
+                       <name>renderShowLink</name>
+                       <description>Whether to render (default: true) "show product" link.</description>
+                       <required>false</required>
+                       <type>java.langBoolean</type>
+               </attribute>
+               <attribute>
+                       <name>rendered</name>
+                       <description>Whether this tag is being rendered by JSF engine (default: true).</description>
+                       <required>false</required>
+                       <type>java.lang.Boolean</type>
+               </attribute>
+       </tag>
+</facelet-taglib>
diff --git a/web/WEB-INF/resources/tags/admin/links/mini/category/admin_product_category_links.tpl b/web/WEB-INF/resources/tags/admin/links/mini/category/admin_product_category_links.tpl
new file mode 100644 (file)
index 0000000..b988c21
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<ui:composition
+       xmlns="http://www.w3.org/1999/xhtml"
+       xmlns:f="http://java.sun.com/jsf/core"
+       xmlns:h="http://java.sun.com/jsf/html"
+       xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+       xmlns:p="http://primefaces.org/ui">
+
+       <ui:fragment rendered="#{empty rendered or rendered}">
+               <ul class="navbar-mini">
+                       <ui:fragment rendered="#{empty renderShowLink or renderShowLink}">
+                               <li class="navlink-mini">
+                                       <p:link outcome="admin_show_product_category" value="#{msg.ADMIN_LINK_SHOW_SHORT}" title="#{msg.ADMIN_LINK_SHOW_PRODUCT_CATEGORY_TITLE}">
+                                               <f:param name="categoryId" value="#{category.categoryId}" />
+                                       </p:link>
+                               </li>
+                       </ui:fragment>
+
+                       <li class="navlink-mini">
+                               <p:link outcome="admin_edit_product_category" value="#{msg.ADMIN_LINK_EDIT_SHORT}" title="#{msg.ADMIN_LINK_EDIT_PRODUCT_CATEGORY_TITLE}">
+                                       <f:param name="categoryId" value="#{category.categoryId}" />
+                               </p:link>
+                       </li>
+
+                       <li class="navlink-mini">
+                               <p:link outcome="admin_delete_product_category">
+                                       <h:outputText styleClass="link-danger" value="#{msg.ADMIN_LINK_DELETE_SHORT}" title="#{msg.ADMIN_LINK_DELETE_PRODUCT_CATEGORY_TITLE}" />
+                                       <f:param name="categoryId" value="#{category.categoryId}" />
+                               </p:link>
+                       </li>
+               </ul>
+       </ui:fragment>
+</ui:composition>
diff --git a/web/WEB-INF/resources/tags/admin/links/mini/product/admin_generic_product_links.tpl b/web/WEB-INF/resources/tags/admin/links/mini/product/admin_generic_product_links.tpl
new file mode 100644 (file)
index 0000000..8d748ae
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<ui:composition
+       xmlns="http://www.w3.org/1999/xhtml"
+       xmlns:f="http://java.sun.com/jsf/core"
+       xmlns:h="http://java.sun.com/jsf/html"
+       xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+       xmlns:p="http://primefaces.org/ui">
+
+       <ui:fragment rendered="#{empty rendered or rendered}">
+               <ul class="navbar-mini">
+                       <ui:fragment rendered="#{empty renderShowLink or renderShowLink}">
+                               <li class="navlink-mini">
+                                       <p:link outcome="admin_show_product" value="#{msg.ADMIN_LINK_SHOW_SHORT}" title="#{msg.ADMIN_LINK_SHOW_GENERIC_PRODUCT_TITLE}">
+                                               <f:param name="productId" value="#{product.productId}" />
+                                       </p:link>
+                               </li>
+                       </ui:fragment>
+
+                       <li class="navlink-mini">
+                               <p:link outcome="admin_edit_product" value="#{msg.ADMIN_LINK_EDIT_SHORT}" title="#{msg.ADMIN_LINK_EDIT_GENERIC_PRODUCT_TITLE}">
+                                       <f:param name="productId" value="#{product.productId}" />
+                               </p:link>
+                       </li>
+
+                       <li class="navlink-mini">
+                               <p:link outcome="admin_delete_product">
+                                       <h:outputText styleClass="link-danger" value="#{msg.ADMIN_LINK_DELETE_SHORT}" title="#{msg.ADMIN_LINK_DELETE_GENERIC_PRODUCT_TITLE}" />
+                                       <f:param name="productId" value="#{product.productId}" />
+                               </p:link>
+                       </li>
+               </ul>
+       </ui:fragment>
+</ui:composition>
diff --git a/web/WEB-INF/templates/admin/category/admin_form_category_data.tpl b/web/WEB-INF/templates/admin/category/admin_form_category_data.tpl
new file mode 100644 (file)
index 0000000..c4d265c
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+@TODO: title="#{project.ADMIN_PRODUCT_CATEGORY_DATA_LEGEND_TITLE}"
+-->
+<ui:composition
+       xmlns="http://www.w3.org/1999/xhtml"
+       xmlns:f="http://xmlns.jcp.org/jsf/core"
+       xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+       xmlns:p="http://primefaces.org/ui">
+
+       <p:fieldset legend="#{project.ADMIN_PRODUCT_CATEGORY_DATA_LEGEND}">
+               <p:panelGrid layout="grid" columns="2" columnClasses="ui-grid-col-4,ui-grid-col-8" styleClass="table table-full ui-noborder">
+                       <p:outputLabel for="parentCategory" value="#{project.ADMIN_ASSIGN_PARENT_CATEGORY}" />
+                       <p:selectOneMenu
+                               id="parentCategory"
+                               value="#{adminCategoryController.parentCategory}"
+                               filter="true"
+                               filterMatchMode="contains"
+                               title="#{project.ADMIN_ASSIGN_PARENT_CATEGORY_TITLE}"
+                               >
+                               <f:converter converterId="ProductCategoryConverter" />
+                               <f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
+                               <f:selectItems value="#{categoryController.allCategories()}" var="category" itemValue="#{category}" itemLabel="#{beanHelper.renderProductCategory(category)}" />
+                       </p:selectOneMenu>
+
+                       <p:outputLabel for="categoryI18nKey" value="#{project.ADMIN_ENTER_CATEGORY_I18N_KEY}" />
+                       <p:inputText
+                               id="categoryI18nKey"
+                               value="#{adminCategoryController.categoryI18nKey}"
+                               size="10"
+                               maxlength="255"
+                               required="true"
+                               requiredMessage="#{project.ADMIN_CATEGORY_I18N_KEY_REQUIRED}"
+                               title="#{project.ADMIN_ENTER_CATEGORY_I18N_KEY_TITLE}"
+                               />
+
+                       <p:outputLabel for="categoryShownInStatistics" value="#{project.ADMIN_ENABLE_CATEGORY_IN_STATISTICS}" />
+                       <p:selectBooleanCheckbox
+                               id="categoryShownInStatistics"
+                               value="#{adminCategoryController.categoryShownInStatistics}"
+                               required="true"
+                               requiredMessage="#{project.ADMIN_ENABLE_CATEGORY_IN_STATISTICS_REQUIRED}"
+                               title="#{project.ADMIN_ENABLE_CATEGORY_IN_STATISTICS_TITLE}"
+                               />
+               </p:panelGrid>
+       </p:fieldset>
+</ui:composition>
index a5f68a54e3a31dd81a6dbe12161aeef0198a776a..887a46b75f3178cb4e9685ef899bd50945e59a2e 100644 (file)
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <ui:composition
        xmlns="http://www.w3.org/1999/xhtml"
-       xmlns:f="http://xmlns.jcp.org/jsf/core"
-       xmlns:h="http://xmlns.jcp.org/jsf/html"
        xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
        xmlns:p="http://primefaces.org/ui">
 
@@ -10,4 +8,9 @@
                <p:menuitem title="#{project.ADMIN_LINK_LIST_RECEIPTS_TITLE}" outcome="admin_list_receipts" value="#{project.ADMIN_LINK_LIST_RECEIPTS}" />
                <p:menuitem title="#{project.ADMIN_LINK_LIST_RECEIPT_ITEMS_TITLE}" outcome="admin_list_receipt_items" value="#{project.ADMIN_LINK_LIST_RECEIPT_ITEMS}" />
        </p:submenu>
+
+       <p:submenu label="#{project.ADMIN_MENU_PRODUCTS_CATEGORIES_TITLE}">
+               <p:menuitem title="#{project.ADMIN_LINK_LIST_GENERIC_PRODUCTS_TITLE}" outcome="admin_list_product" value="#{project.ADMIN_LINK_LIST_GENERIC_PRODUCTS}" />
+               <p:menuitem title="#{project.ADMIN_LINK_LIST_CATEGORIES_TITLE}" outcome="admin_list_category" value="#{project.ADMIN_LINK_LIST_CATEGORIES}" />
+       </p:submenu>
 </ui:composition>
diff --git a/web/WEB-INF/templates/admin/product/admin_form_product_data.tpl b/web/WEB-INF/templates/admin/product/admin_form_product_data.tpl
new file mode 100644 (file)
index 0000000..2587060
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+@TODO: title="#{project.ADMIN_GENERIC_PRODUCT_DATA_LEGEND_TITLE}"
+-->
+<ui:composition
+       xmlns="http://www.w3.org/1999/xhtml"
+       xmlns:product="http://mxchange.org/jsf/jproduct/widgets"
+       xmlns:f="http://xmlns.jcp.org/jsf/core"
+       xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+       xmlns:p="http://primefaces.org/ui">
+
+       <p:fieldset legend="#{project.ADMIN_GENERIC_PRODUCT_DATA_LEGEND}">
+               <p:panelGrid layout="grid" columns="2" columnClasses="ui-grid-col-4,ui-grid-col-8" styleClass="table table-full ui-noborder">
+                       <p:outputLabel for="productCategory" value="#{project.ADMIN_ASSIGN_PRODUCT_CATEGORY}" />
+                       <p:selectOneMenu
+                               id="productCategory"
+                               value="#{adminProductController.productCategory}"
+                               filter="true"
+                               filterMatchMode="contains"
+                               required="true"
+                               requiredMessage="#{project.ADMIN_PRODUCT_CATEGORY_REQUIRED}"
+                               title="#{project.ADMIN_ASSIGN_PRODUCT_CATEGORY_TITLE}"
+                               >
+                               <f:converter converterId="ProductCategoryConverter" />
+                               <f:selectItem itemValue="#{null}" itemLabel="#{msg.PLEASE_SELECT}" noSelectionOption="true" itemDisabled="true" />
+                               <f:selectItems value="#{categoryController.allCategories()}" var="category" itemValue="#{category}" itemLabel="#{beanHelper.renderProductCategory(category)}" />
+                       </p:selectOneMenu>
+
+                       <p:outputLabel for="productI18nKey" value="#{project.ADMIN_ENTER_PRODUCT_I18N_KEY}" />
+                       <p:inputText
+                               id="productI18nKey"
+                               value="#{adminProductController.productI18nKey}"
+                               size="10"
+                               maxlength="255"
+                               required="true"
+                               requiredMessage="#{project.ADMIN_PRODUCT_I18N_KEY_REQUIRED}"
+                               title="#{project.ADMIN_ENTER_PRODUCT_I18N_KEY_TITLE}"
+                               />
+
+                       <p:outputLabel for="productAvailability" value="#{project.ADMIN_ENABLE_PRODUCT_AVAILABILITY}" />
+                       <p:selectBooleanCheckbox
+                               id="productAvailability"
+                               value="#{adminProductController.productAvailability}"
+                               required="true"
+                               requiredMessage="#{project.ADMIN_GENERIC_PROJECT_AVAILABILITY_REQUIRED}"
+                               title="#{project.ADMIN_ENABLE_PRODUCT_AVAILABILITY_TITLE}"
+                               />
+
+                       <p:outputLabel value="#{project.ADMIN_ENTER_PRODUCT_PRICE}" />
+                       <product:inputProductPricePanelGrid targetController="#{adminProductController}" />
+
+                       <p:outputLabel for="productCurrencyCode" value="#{project.ADMIN_ENTER_PRODUCT_CURRENCY_CODE}" />
+                       <p:inputText
+                               id="productCurrencyCode"
+                               value="#{adminProductController.productCurrencyCode}"
+                               size="3"
+                               maxlength="3"
+                               title="#{project.ADMIN_ENTER_PRODUCT_CURRENCY_CODE_TITLE}"
+                               />
+
+                       <p:outputLabel for="productUnitAmount" value="#{project.ADMIN_ENTER_PRODUCT_UNIT_AMOUNT}" />
+                       <p:inputNumber
+                               id="productUnitAmount"
+                               value="#{adminProductController.productUnitAmount}"
+                               decimalSeparator=","
+                               thousandSeparator="."
+                               title="#{project.ADMIN_ENTER_PRODUCT_UNIT_AMOUNT_TITLE}"
+                               />
+
+                       <p:outputLabel for="productUnitType" value="#{project.ADMIN_ENTER_PRODUCT_UNIT_TYPE}" />
+                       <p:inputText
+                               id="productUnitType"
+                               value="#{adminProductController.productUnitType}"
+                               size="10"
+                               maxlength="255"
+                               title="#{project.ADMIN_ENTER_PRODUCT_UNIT_TYPE_TITLE}"
+                               />
+               </p:panelGrid>
+       </p:fieldset>
+</ui:composition>
index dbce861c592d5c85b01ac24d3b8df585669844a4..3e85fbc2c818ed9a77b9ecefe503d4c1106aa674 100644 (file)
@@ -30,7 +30,7 @@
     <context-param>
         <description>Generic custom JSF tags library</description>
         <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
-        <param-value>/WEB-INF/widgets.jsf.taglib.xml;/WEB-INF/links.jsf.taglib.xml;/WEB-INF/project-links.jsf.taglib.xml;/WEB-INF/product.jsf.taglib.xml</param-value>
+        <param-value>/WEB-INF/widgets.jsf.taglib.xml;/WEB-INF/links.jsf.taglib.xml;/WEB-INF/project-links.jsf.taglib.xml;/WEB-INF/product.jsf.taglib.xml;/WEB-INF/product-links.jsf.taglib.xml</param-value>
     </context-param>
     <context-param>
         <description>Project stage</description>
diff --git a/web/admin/category/admin_product_category_list.xhtml b/web/admin/category/admin_product_category_list.xhtml
new file mode 100644 (file)
index 0000000..1796183
--- /dev/null
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<ui:composition template="/WEB-INF/templates/admin/admin_base.tpl"
+                               xmlns="http://www.w3.org/1999/xhtml"
+                               xmlns:product-links="http://mxchange.org/jsf/jproduct/links"
+                               xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+                               xmlns:h="http://xmlns.jcp.org/jsf/html"
+                               xmlns:f="http://xmlns.jcp.org/jsf/core"
+                               xmlns:p="http://primefaces.org/ui">
+
+       <ui:define name="document_admin_title">
+               <h:outputText value="#{project.PAGE_TITLE_ADMIN_LIST_PRODUCT_CATEGORY}" />
+       </ui:define>
+
+       <ui:define name="content_header">
+               <h:outputText value="#{project.CONTENT_TITLE_ADMIN_LIST_PRODUCT_CATEGORY}" />
+       </ui:define>
+
+       <ui:define name="content">
+               <h:form id="form-list-categories">
+                       <p:dataTable
+                               id="table-list-categories"
+                               var="category"
+                               value="#{categoryController.allCategories()}"
+                               tableStyleClass="table table-full"
+                               paginator="true"
+                               paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
+                               filteredValue="#{categoryController.filteredCategories}"
+                               rows="10"
+                               reflow="true"
+                               resizableColumns="true"
+                               rowsPerPageTemplate="5,10,20,50,100"
+                               sortMode="multiple"
+                               summary="#{project.TABLE_SUMMARY_ADMIN_LIST_PRODUCT_CATEGORIES}"
+                               emptyMessage="#{project.ADMIN_EMPTY_LIST_PRODUCT_CATEGORIES}"
+                               widgetVar="categoryList"
+                               >
+
+                               <f:facet name="header">
+                                       <p:panelGrid columns="2" columnClasses="ui-grid-col-10,ui-grid-col-2" layout="grid" styleClass="ui-noborder ui-transparent-widget">
+                                               <h:outputText value="#{project.ADMIN_LIST_PRODUCT_CATEGORIES_HEADER}" />
+
+                                               <h:panelGroup>
+                                                       <p:commandButton id="toggler" type="button" value="#{msg.SELECT_SHOWN_COLUMNS}" styleClass="column-selector" />
+                                                       <p:columnToggler datasource="table-list-categories" trigger="toggler" />
+                                               </h:panelGroup>
+                                       </p:panelGrid>
+                               </f:facet>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_ID_NUMBER}" sortBy="#{category.categoryId}" filterable="false">
+                                       <p:link outcome="admin_show_product_category" title="#{project.ADMIN_LINK_SHOW_PRODUCT_CATEGORY_TITLE}" value="#{category.categoryId}">
+                                               <f:param name="categoryId" value="#{category.categoryId}" />
+                                       </p:link>
+                               </p:column>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_I18N_KEY}" sortBy="#{category.categoryI18nKey}" filterBy="#{category.categoryI18nKey}" filterMatchMode="contains">
+                                       <h:outputText value="#{local[category.categoryI18nKey]}" />
+                               </p:column>
+
+                               <p:column headerText="#{project.ADMIN_HEADER_ASSIGNED_PARENT_CATEGORY}" sortBy="#{category.parentCategory.categoryI18nKey}" filterBy="#{category.parentCategory}" filterMatchMode="in">
+                                       <f:facet name="filter">
+                                               <p:selectCheckboxMenu
+                                                       filter="true"
+                                                       filterMatchMode="contains"
+                                                       label="#{project.LABEL_PRODUCT_CATEGORIES}"
+                                                       onchange="PF('categoryList').filter()"
+                                                       updateLabel="true"
+                                                       title="#{project.FILTER_BY_MULTIPLE_PRODUCT_CATEGORIES_TITLE}"
+                                                       >
+                                                       <f:converter converterId="ProductCategoryConverter" />
+                                                       <f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
+                                                       <f:selectItems value="#{categoryController.allCategories()}" var="category" itemValue="#{category}" itemLabel="#{beanHelper.renderProductCategory(category)}" />
+                                               </p:selectCheckboxMenu>
+                                       </f:facet>
+
+                                       <p:link outcome="admin_show_product_category" title="#{project.ADMIN_LINK_SHOW_PRODUCT_CATEGORY_TITLE}" value="#{category.parentCategory.categoryId}" rendered="#{not empty category.parentCategory}">
+                                               <f:param name="categoryId" value="#{category.parentCategory.categoryId}" />
+                                       </p:link>
+
+                                       <p:link outcome="admin_assign_parent_category" title="#{project.ADMIN_LINK_ASSIGN_PARENT_CATEGORY_TITLE}" value="#{msg.ADMIN_NOT_ASSIGNED}" rendered="#{empty category.parentCategory}">
+                                               <f:param name="categoryId" value="#{category.categoryId}" />
+                                       </p:link>
+                               </p:column>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_ENTRY_CREATED}" sortBy="#{category.categoryCreated}" filterable="false">
+                                       <h:outputText id="categoryCreated" value="#{category.categoryCreated.time}">
+                                               <f:convertDateTime for="categoryCreated" type="both" timeStyle="short" dateStyle="short" />
+                                       </h:outputText>
+                               </p:column>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_ACTION_LINKS}" sortable="false" filterable="false">
+                                       <product-links:outputProductCategoryAdminMiniLinks category="#{category}" />
+                               </p:column>
+                       </p:dataTable>
+               </h:form>
+
+               <h:form>
+                       <p:panelGrid columns="1" styleClass="table table-full" layout="grid">
+                               <f:facet name="header">
+                                       <h:outputText value="#{project.ADMIN_ADD_PRODUCT_CATEGORY_TITLE}" />
+                               </f:facet>
+
+                               <h:panelGroup styleClass="para" layout="block">
+                                       <h:outputText value="#{project.ADMIN_ADD_PRODUCT_CATEGORY_MINIMUM_DATA}" />
+                               </h:panelGroup>
+
+                               <ui:include src="/WEB-INF/templates/admin/category/admin_form_category_data.tpl" />
+
+                               <f:facet name="footer">
+                                       <p:panelGrid columns="2" layout="grid">
+                                               <p:commandButton
+                                                       styleClass="reset"
+                                                       type="reset"
+                                                       value="#{msg.BUTTON_RESET_FORM}"
+                                                       />
+
+                                               <p:commandButton
+                                                       styleClass="submit"
+                                                       type="submit"
+                                                       value="#{project.BUTTON_ADMIN_ADD_PRODUCT_CATEGORY}"
+                                                       action="#{adminCategoryController.addCategory()}"
+                                                       update=":master:form-list-categories:table-list-categories"
+                                                       />
+                                       </p:panelGrid>
+                               </f:facet>
+                       </p:panelGrid>
+               </h:form>
+       </ui:define>
+</ui:composition>
diff --git a/web/admin/product/admin_generic_product_list.xhtml b/web/admin/product/admin_generic_product_list.xhtml
new file mode 100644 (file)
index 0000000..bcaa819
--- /dev/null
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<ui:composition template="/WEB-INF/templates/admin/admin_base.tpl"
+                               xmlns="http://www.w3.org/1999/xhtml"
+                               xmlns:product-links="http://mxchange.org/jsf/jproduct/links"
+                               xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+                               xmlns:h="http://xmlns.jcp.org/jsf/html"
+                               xmlns:f="http://xmlns.jcp.org/jsf/core"
+                               xmlns:p="http://primefaces.org/ui">
+
+       <ui:define name="document_admin_title">
+               <h:outputText value="#{project.PAGE_TITLE_ADMIN_LIST_GENERIC_PRODUCT}" />
+       </ui:define>
+
+       <ui:define name="content_header">
+               <h:outputText value="#{project.CONTENT_TITLE_ADMIN_LIST_GENERIC_PRODUCT}" />
+       </ui:define>
+
+       <ui:define name="content">
+               <h:form id="form-list-products">
+                       <p:dataTable
+                               id="table-list-products"
+                               var="product"
+                               value="#{productController.allProducts()}"
+                               tableStyleClass="table table-full"
+                               paginator="true"
+                               paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
+                               filteredValue="#{productController.filteredProducts}"
+                               rows="10"
+                               reflow="true"
+                               resizableColumns="true"
+                               rowsPerPageTemplate="5,10,20,50,100"
+                               sortMode="multiple"
+                               summary="#{project.TABLE_SUMMARY_ADMIN_LIST_GENERIC_PRODUCTS}"
+                               emptyMessage="#{project.ADMIN_EMPTY_LIST_GENERIC_PRODUCTS}"
+                               widgetVar="productList"
+                               >
+
+                               <f:facet name="header">
+                                       <p:panelGrid columns="2" columnClasses="ui-grid-col-10,ui-grid-col-2" layout="grid" styleClass="ui-noborder ui-transparent-widget">
+                                               <h:outputText value="#{project.ADMIN_LIST_GENERIC_PRODUCTS_HEADER}" />
+
+                                               <h:panelGroup>
+                                                       <p:commandButton id="toggler" type="button" value="#{msg.SELECT_SHOWN_COLUMNS}" styleClass="column-selector" />
+                                                       <p:columnToggler datasource="table-list-products" trigger="toggler" />
+                                               </h:panelGroup>
+                                       </p:panelGrid>
+                               </f:facet>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_ID_NUMBER}" sortBy="#{product.productId}" filterable="false">
+                                       <p:link outcome="admin_show_generic_product" title="#{project.ADMIN_LINK_SHOW_GENERIC_PRODUCT_TITLE}" value="#{product.productId}">
+                                               <f:param name="productId" value="#{product.productId}" />
+                                       </p:link>
+                               </p:column>
+
+                               <p:column headerText="#{project.ADMIN_HEADER_I18N_KEY}" sortBy="#{product.productI18nKey}" filterBy="#{product.productI18nKey}" filterMatchMode="contains">
+                                       <h:outputText value="#{product.productI18nKey}" />
+                               </p:column>
+
+                               <p:column headerText="#{project.ADMIN_HEADER_ASSIGNED_PRODUCT_CATEGORY}" sortBy="#{product.productCategory.categoryI18nKey}" filterBy="#{product.productCategory}" filterMatchMode="in">
+                                       <f:facet name="filter">
+                                               <p:selectCheckboxMenu
+                                                       filter="true"
+                                                       filterMatchMode="contains"
+                                                       label="#{project.LABEL_PRODUCT_CATEGORIES}"
+                                                       onchange="PF('productList').filter()"
+                                                       updateLabel="true"
+                                                       title="#{project.FILTER_BY_MULTIPLE_PRODUCT_CATEGORIES_TITLE}"
+                                                       >
+                                                       <f:converter converterId="ProductCategoryConverter" />
+                                                       <f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
+                                                       <f:selectItems value="#{categoryController.allCategories()}" var="category" itemValue="#{category}" itemLabel="#{beanHelper.renderProductCategory(category)}" />
+                                               </p:selectCheckboxMenu>
+                                       </f:facet>
+
+                                       <p:link outcome="admin_show_product_category" title="#{project.ADMIN_LINK_SHOW_GENERIC_PRODUCT_CATEGORY_TITLE}" value="#{product.productCategory.categoryId}">
+                                               <f:param name="categoryId" value="#{product.productCategory.categoryId}" />
+                                       </p:link>
+                               </p:column>
+
+                               <p:column headerText="#{project.ADMIN_HEADER_PRODUCT_GROSS_PRICE}" sortBy="#{product.productAvailability}" filterBy="#{product.productGrossPrice}" filterMatchMode="choose">
+                                       <h:outputText value="#{product.productGrossPrice}">
+                                               <!-- @TODO Hard-coded EUR again -->
+                                               <f:convertNumber type="currency" currencyCode="EUR" />
+                                       </h:outputText>
+                               </p:column>
+
+                               <p:column headerText="#{project.ADMIN_HEADER_PRODUCT_AVAILABILITY}" sortBy="#{product.productAvailability}" filterBy="#{product.productAvailability}" filterMatchMode="">
+                                       <h:outputText value="#{product.productAvailability ? msg.CHOICE_YES : msg.CHOICE_NO}" />
+                               </p:column>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_ENTRY_CREATED}" sortBy="#{product.productCreated}" filterable="false">
+                                       <h:outputText id="productCreated" value="#{product.productCreated.time}">
+                                               <f:convertDateTime for="productCreated" type="both" timeStyle="short" dateStyle="short" />
+                                       </h:outputText>
+                               </p:column>
+
+                               <p:column headerText="#{msg.ADMIN_HEADER_ACTION_LINKS}" sortable="false" filterable="false">
+                                       <product-links:outputGenericProductAdminMiniLinks product="#{product}" />
+                               </p:column>
+                       </p:dataTable>
+               </h:form>
+
+               <h:form>
+                       <p:panelGrid columns="1" styleClass="table table-full" layout="grid">
+                               <f:facet name="header">
+                                       <h:outputText value="#{project.ADMIN_ADD_GENERIC_PRODUCT_TITLE}" />
+                               </f:facet>
+
+                               <h:panelGroup styleClass="para" layout="block">
+                                       <h:outputText value="#{project.ADMIN_ADD_GENERIC_PRODUCT_MINIMUM_DATA}" />
+                               </h:panelGroup>
+
+                               <ui:include src="/WEB-INF/templates/admin/product/admin_form_product_data.tpl" />
+
+                               <f:facet name="footer">
+                                       <p:panelGrid columns="2" layout="grid">
+                                               <p:commandButton
+                                                       styleClass="reset"
+                                                       type="reset"
+                                                       value="#{msg.BUTTON_RESET_FORM}"
+                                                       />
+
+                                               <p:commandButton
+                                                       styleClass="submit"
+                                                       type="submit"
+                                                       value="#{project.BUTTON_ADMIN_ADD_GENERIC_PRODUCT}"
+                                                       action="#{adminProductController.addProduct()}"
+                                                       update=":master:form-list-products:table-list-products"
+                                                       />
+                                       </p:panelGrid>
+                               </f:facet>
+                       </p:panelGrid>
+               </h:form>
+       </ui:define>
+</ui:composition>