├── .gitignore ├── LICENSE ├── README.md ├── nb-configuration.xml ├── pom.xml └── src └── main ├── java ├── cart │ ├── ShoppingCart.java │ └── ShoppingCartItem.java ├── controller │ ├── AdminServlet.java │ └── ControllerServlet.java ├── entity │ ├── Category.java │ ├── Customer.java │ ├── CustomerOrder.java │ ├── OrderedProduct.java │ ├── OrderedProductPK.java │ ├── Product.java │ └── dbschema.sql ├── filter │ └── SessionTimeoutFilter.java ├── resources │ ├── messages.properties │ ├── messages_cs.properties │ └── messages_en.properties ├── rest │ ├── ApplicationResource.java │ ├── DepartmentService.java │ └── PurchaseService.java ├── session │ ├── AbstractFacade.java │ ├── CategoryFacade.java │ ├── CustomerFacade.java │ ├── CustomerOrderFacade.java │ ├── OrderManager.java │ ├── OrderedProductFacade.java │ └── ProductFacade.java └── validate │ └── Validator.java ├── resources └── META-INF │ └── persistence.xml ├── setup ├── README.txt ├── glassfish-resources.xml ├── sampleData.sql └── schema.sql └── webapp ├── WEB-INF ├── glassfish-web.xml └── web.xml ├── css ├── app.css ├── bootstrap-responsive.css ├── bootstrap-responsive.min.css ├── bootstrap.css └── bootstrap.min.css ├── img ├── cart.gif ├── categories │ ├── bakery.jpg │ ├── dairy.jpg │ ├── fruitveg.jpg │ └── meats.jpg ├── favicon.ico ├── glyphicons-halflings-white.png ├── glyphicons-halflings.png ├── logo.png ├── logoText.png ├── products │ ├── broccoli.png │ ├── butter.png │ ├── cheese.png │ ├── chicken leg.png │ ├── chocolate cookies.png │ ├── corn on the cob.png │ ├── free range eggs.png │ ├── milk.png │ ├── organic meat patties.png │ ├── parma ham.png │ ├── pumpkin seed bun.png │ ├── red currants.png │ ├── sausages.png │ ├── seedless watermelon.png │ ├── sesame seed bagel.png │ └── sunflower seed loaf.png └── stalk.png ├── index.html ├── js ├── app.js ├── controllers.js ├── directives.js ├── filters.js └── services.js ├── json ├── 1.json ├── 2.json ├── 3.json └── 4.json ├── lib ├── angular │ ├── angular-cookies.js │ ├── angular-cookies.min.js │ ├── angular-loader.js │ ├── angular-loader.min.js │ ├── angular-resource.js │ ├── angular-resource.min.js │ ├── angular-sanitize.js │ ├── angular-sanitize.min.js │ ├── angular.js │ ├── angular.min.js │ └── version.txt ├── bootstrap │ ├── bootstrap.js │ └── bootstrap.min.js └── jquery │ └── jquery-1.8.0.js └── partials ├── cart.html ├── checkout.html ├── department.html └── main.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | /target/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Petr Jiricka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | affablebean-angularjs-ee7 2 | ========================= 3 | 4 | Affable Bean shopping cart application rewritten to AngularJS frontend and Java EE 7 backend. 5 | -------------------------------------------------------------------------------- /nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 1.7-web 17 | gfv3ee6 18 | ide 19 | 20 | 21 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.mycompany 6 | AffableBean 7 | 1.0-SNAPSHOT 8 | war 9 | 10 | AffableBean 11 | 12 | 13 | ${project.build.directory}/endorsed 14 | UTF-8 15 | 16 | 17 | 18 | 19 | org.eclipse.persistence 20 | eclipselink 21 | 2.5.2 22 | provided 23 | 24 | 25 | org.eclipse.persistence 26 | org.eclipse.persistence.jpa.modelgen.processor 27 | 2.5.2 28 | provided 29 | 30 | 31 | javax 32 | javaee-web-api 33 | 7.0 34 | provided 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-compiler-plugin 43 | 3.1 44 | 45 | 1.7 46 | 1.7 47 | 48 | ${endorsed.dir} 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-war-plugin 55 | 2.3 56 | 57 | false 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-dependency-plugin 63 | 2.6 64 | 65 | 66 | validate 67 | 68 | copy 69 | 70 | 71 | ${endorsed.dir} 72 | true 73 | 74 | 75 | javax 76 | javaee-endorsed-api 77 | 7.0 78 | jar 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | http://download.eclipse.org/rt/eclipselink/maven.repo/ 90 | eclipselink 91 | default 92 | Repository for library EclipseLink (JPA 2.1) 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/main/java/cart/ShoppingCart.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package cart; 10 | 11 | import entity.Product; 12 | import java.util.*; 13 | 14 | /** 15 | * 16 | * @author tgiunipero 17 | */ 18 | public class ShoppingCart { 19 | 20 | List items; 21 | int numberOfItems; 22 | double total; 23 | 24 | public ShoppingCart() { 25 | items = new ArrayList(); 26 | numberOfItems = 0; 27 | total = 0; 28 | } 29 | 30 | /** 31 | * Adds a ShoppingCartItem to the ShoppingCart's 32 | * items list. If item of the specified product 33 | * already exists in shopping cart list, the quantity of that item is 34 | * incremented. 35 | * 36 | * @param product the Product that defines the type of shopping cart item 37 | * @see ShoppingCartItem 38 | */ 39 | public synchronized void addItem(Product product) { 40 | 41 | boolean newItem = true; 42 | 43 | for (ShoppingCartItem scItem : items) { 44 | 45 | if (scItem.getProduct().getId() == product.getId()) { 46 | 47 | newItem = false; 48 | scItem.incrementQuantity(); 49 | } 50 | } 51 | 52 | if (newItem) { 53 | ShoppingCartItem scItem = new ShoppingCartItem(product); 54 | items.add(scItem); 55 | } 56 | } 57 | 58 | /** 59 | * Updates the ShoppingCartItem of the specified 60 | * product to the specified quantity. If '0' 61 | * is the given quantity, the ShoppingCartItem is removed 62 | * from the ShoppingCart's items list. 63 | * 64 | * @param product the Product that defines the type of shopping cart item 65 | * @param quantity the number which the ShoppingCartItem is updated to 66 | * @see ShoppingCartItem 67 | */ 68 | public synchronized void update(Product product, String quantity) { 69 | 70 | // cast quantity as short 71 | short qty = Short.parseShort(quantity); 72 | 73 | if (qty >= 0) { 74 | 75 | ShoppingCartItem item = null; 76 | 77 | for (ShoppingCartItem scItem : items) { 78 | 79 | if (scItem.getProduct().getId() == product.getId()) { 80 | 81 | if (qty != 0) { 82 | // set item quantity to new value 83 | scItem.setQuantity(qty); 84 | } else { 85 | // if quantity equals 0, save item and break 86 | item = scItem; 87 | break; 88 | } 89 | } 90 | } 91 | 92 | if (item != null) { 93 | // remove from cart 94 | items.remove(item); 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * Returns the list of ShoppingCartItems. 101 | * 102 | * @return the items list 103 | * @see ShoppingCartItem 104 | */ 105 | public synchronized List getItems() { 106 | 107 | return items; 108 | } 109 | 110 | /** 111 | * Returns the sum of quantities for all items maintained in shopping cart 112 | * items list. 113 | * 114 | * @return the number of items in shopping cart 115 | * @see ShoppingCartItem 116 | */ 117 | public synchronized int getNumberOfItems() { 118 | 119 | numberOfItems = 0; 120 | 121 | for (ShoppingCartItem scItem : items) { 122 | 123 | numberOfItems += scItem.getQuantity(); 124 | } 125 | 126 | return numberOfItems; 127 | } 128 | 129 | /** 130 | * Returns the sum of the product price multiplied by the quantity for all 131 | * items in shopping cart list. This is the total cost excluding the surcharge. 132 | * 133 | * @return the cost of all items times their quantities 134 | * @see ShoppingCartItem 135 | */ 136 | public synchronized double getSubtotal() { 137 | 138 | double amount = 0; 139 | 140 | for (ShoppingCartItem scItem : items) { 141 | 142 | Product product = (Product) scItem.getProduct(); 143 | amount += (scItem.getQuantity() * product.getPrice().doubleValue()); 144 | } 145 | 146 | return amount; 147 | } 148 | 149 | /** 150 | * Calculates the total cost of the order. This method adds the subtotal to 151 | * the designated surcharge and sets the total instance variable 152 | * with the result. 153 | * 154 | * @param surcharge the designated surcharge for all orders 155 | * @see ShoppingCartItem 156 | */ 157 | public synchronized void calculateTotal(String surcharge) { 158 | 159 | double amount = 0; 160 | 161 | // cast surcharge as double 162 | double s = Double.parseDouble(surcharge); 163 | 164 | amount = this.getSubtotal(); 165 | amount += s; 166 | 167 | total = amount; 168 | } 169 | 170 | /** 171 | * Returns the total cost of the order for the given 172 | * ShoppingCart instance. 173 | * 174 | * @return the cost of all items times their quantities plus surcharge 175 | */ 176 | public synchronized double getTotal() { 177 | 178 | return total; 179 | } 180 | 181 | /** 182 | * Empties the shopping cart. All items are removed from the shopping cart 183 | * items list, numberOfItems and 184 | * total are reset to '0'. 185 | * 186 | * @see ShoppingCartItem 187 | */ 188 | public synchronized void clear() { 189 | items.clear(); 190 | numberOfItems = 0; 191 | total = 0; 192 | } 193 | 194 | } -------------------------------------------------------------------------------- /src/main/java/cart/ShoppingCartItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package cart; 10 | 11 | import entity.Product; 12 | 13 | /** 14 | * 15 | * @author tgiunipero 16 | */ 17 | public class ShoppingCartItem { 18 | 19 | Product product; 20 | short quantity; 21 | 22 | public ShoppingCartItem(Product product) { 23 | this.product = product; 24 | quantity = 1; 25 | } 26 | 27 | public Product getProduct() { 28 | return product; 29 | } 30 | 31 | public short getQuantity() { 32 | return quantity; 33 | } 34 | 35 | public void setQuantity(short quantity) { 36 | this.quantity = quantity; 37 | } 38 | 39 | public void incrementQuantity() { 40 | quantity++; 41 | } 42 | 43 | public void decrementQuantity() { 44 | quantity--; 45 | } 46 | 47 | public double getTotal() { 48 | double amount = 0; 49 | amount = (this.getQuantity() * product.getPrice().doubleValue()); 50 | return amount; 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/controller/AdminServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package controller; 10 | 11 | import entity.Customer; 12 | import entity.CustomerOrder; 13 | import java.io.IOException; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | import javax.ejb.EJB; 18 | import javax.servlet.ServletException; 19 | import javax.servlet.annotation.HttpConstraint; 20 | import javax.servlet.annotation.ServletSecurity; 21 | import javax.servlet.annotation.ServletSecurity.TransportGuarantee; 22 | import javax.servlet.annotation.WebServlet; 23 | import javax.servlet.http.*; 24 | import javax.servlet.http.HttpSession; 25 | import session.CustomerFacade; 26 | import session.CustomerOrderFacade; 27 | import session.OrderManager; 28 | 29 | /** 30 | * 31 | * @author tgiunipero 32 | */ 33 | @WebServlet(name = "AdminServlet", 34 | urlPatterns = {"/admin/", 35 | "/admin/viewOrders", 36 | "/admin/viewCustomers", 37 | "/admin/customerRecord", 38 | "/admin/orderRecord", 39 | "/admin/logout"}) 40 | public class AdminServlet extends HttpServlet { 41 | 42 | @EJB 43 | private OrderManager orderManager; 44 | @EJB 45 | private CustomerFacade customerFacade; 46 | @EJB 47 | private CustomerOrderFacade customerOrderFacade; 48 | 49 | private String userPath; 50 | private Customer customer; 51 | private CustomerOrder order; 52 | private List orderList = new ArrayList(); 53 | private List customerList = new ArrayList(); 54 | 55 | 56 | /** 57 | * Processes requests for both HTTP GET and POST methods. 58 | * @param request servlet request 59 | * @param response servlet response 60 | * @throws ServletException if a servlet-specific error occurs 61 | * @throws IOException if an I/O error occurs 62 | */ 63 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 64 | throws ServletException, IOException { 65 | 66 | HttpSession session = request.getSession(true); 67 | userPath = request.getServletPath(); 68 | 69 | // if viewCustomers is requested 70 | if (userPath.equals("/admin/viewCustomers")) { 71 | customerList = customerFacade.findAll(); 72 | request.setAttribute("customerList", customerList); 73 | } 74 | 75 | // if viewOrders is requested 76 | if (userPath.equals("/admin/viewOrders")) { 77 | orderList = customerOrderFacade.findAll(); 78 | request.setAttribute("orderList", orderList); 79 | } 80 | 81 | // if customerRecord is requested 82 | if (userPath.equals("/admin/customerRecord")) { 83 | 84 | // get customer ID from request 85 | String customerId = request.getQueryString(); 86 | 87 | // get customer details 88 | customer = customerFacade.find(Integer.parseInt(customerId)); 89 | request.setAttribute("customerRecord", customer); 90 | 91 | // get customer order details 92 | order = customerOrderFacade.findByCustomer(customer); 93 | request.setAttribute("order", order); 94 | } 95 | 96 | // if orderRecord is requested 97 | if (userPath.equals("/admin/orderRecord")) { 98 | 99 | // get customer ID from request 100 | String orderId = request.getQueryString(); 101 | 102 | // get order details 103 | Map orderMap = orderManager.getOrderDetails(Integer.parseInt(orderId)); 104 | 105 | // place order details in request scope 106 | request.setAttribute("customer", orderMap.get("customer")); 107 | request.setAttribute("products", orderMap.get("products")); 108 | request.setAttribute("orderRecord", orderMap.get("orderRecord")); 109 | request.setAttribute("orderedProducts", orderMap.get("orderedProducts")); 110 | } 111 | 112 | // if logout is requested 113 | if (userPath.equals("/admin/logout")) { 114 | session = request.getSession(); 115 | session.invalidate(); // terminate session 116 | response.sendRedirect("/AffableBean/admin/"); 117 | return; 118 | } 119 | 120 | // use RequestDispatcher to forward request internally 121 | userPath = "/admin/index.jsp"; 122 | try { 123 | request.getRequestDispatcher(userPath).forward(request, response); 124 | } catch (Exception ex) { 125 | ex.printStackTrace(); 126 | } 127 | } 128 | 129 | /** 130 | * Handles the HTTP GET method. 131 | * @param request servlet request 132 | * @param response servlet response 133 | * @throws ServletException if a servlet-specific error occurs 134 | * @throws IOException if an I/O error occurs 135 | */ 136 | @Override 137 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 138 | throws ServletException, IOException { 139 | processRequest(request, response); 140 | } 141 | 142 | /** 143 | * Handles the HTTP POST method. 144 | * @param request servlet request 145 | * @param response servlet response 146 | * @throws ServletException if a servlet-specific error occurs 147 | * @throws IOException if an I/O error occurs 148 | */ 149 | @Override 150 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 151 | throws ServletException, IOException { 152 | processRequest(request, response); 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /src/main/java/controller/ControllerServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package controller; 10 | 11 | import cart.ShoppingCart; 12 | import entity.Category; 13 | import entity.Product; 14 | import java.io.IOException; 15 | import java.util.Collection; 16 | import java.util.Locale; 17 | import java.util.Map; 18 | import javax.ejb.EJB; 19 | import javax.servlet.ServletConfig; 20 | import javax.servlet.ServletException; 21 | import javax.servlet.annotation.WebServlet; 22 | import javax.servlet.http.*; 23 | import session.CategoryFacade; 24 | import session.OrderManager; 25 | import session.ProductFacade; 26 | import validate.Validator; 27 | 28 | /** 29 | * 30 | * @author tgiunipero 31 | */ 32 | @WebServlet(name = "Controller", 33 | loadOnStartup = 1, 34 | urlPatterns = {"/category", 35 | "/addToCart", 36 | "/viewCart", 37 | "/updateCart", 38 | "/checkout", 39 | "/purchase", 40 | "/chooseLanguage"}) 41 | public class ControllerServlet extends HttpServlet { 42 | 43 | private String surcharge; 44 | 45 | @EJB 46 | private CategoryFacade categoryFacade; 47 | @EJB 48 | private ProductFacade productFacade; 49 | @EJB 50 | private OrderManager orderManager; 51 | 52 | 53 | @Override 54 | public void init(ServletConfig servletConfig) throws ServletException { 55 | 56 | super.init(servletConfig); 57 | 58 | // initialize servlet with configuration information 59 | surcharge = servletConfig.getServletContext().getInitParameter("deliverySurcharge"); 60 | 61 | // store category list in servlet context 62 | getServletContext().setAttribute("categories", categoryFacade.findAll()); 63 | } 64 | 65 | 66 | /** 67 | * Handles the HTTP GET method. 68 | * @param request servlet request 69 | * @param response servlet response 70 | * @throws ServletException if a servlet-specific error occurs 71 | * @throws IOException if an I/O error occurs 72 | */ 73 | @Override 74 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 75 | throws ServletException, IOException { 76 | 77 | String userPath = request.getServletPath(); 78 | HttpSession session = request.getSession(); 79 | Category selectedCategory; 80 | Collection categoryProducts; 81 | 82 | 83 | // if category page is requested 84 | if (userPath.equals("/category")) { 85 | 86 | // get categoryId from request 87 | String categoryId = request.getQueryString(); 88 | 89 | if (categoryId != null) { 90 | 91 | // get selected category 92 | selectedCategory = categoryFacade.find(Short.parseShort(categoryId)); 93 | 94 | // place selected category in session scope 95 | session.setAttribute("selectedCategory", selectedCategory); 96 | 97 | // get all products for selected category 98 | categoryProducts = selectedCategory.getProductCollection(); 99 | 100 | // place category products in session scope 101 | session.setAttribute("categoryProducts", categoryProducts); 102 | } 103 | 104 | 105 | // if cart page is requested 106 | } else if (userPath.equals("/viewCart")) { 107 | 108 | String clear = request.getParameter("clear"); 109 | 110 | if ((clear != null) && clear.equals("true")) { 111 | 112 | ShoppingCart cart = (ShoppingCart) session.getAttribute("cart"); 113 | cart.clear(); 114 | } 115 | 116 | userPath = "/cart"; 117 | 118 | 119 | // if checkout page is requested 120 | } else if (userPath.equals("/checkout")) { 121 | 122 | ShoppingCart cart = (ShoppingCart) session.getAttribute("cart"); 123 | 124 | // calculate total 125 | cart.calculateTotal(surcharge); 126 | 127 | // forward to checkout page and switch to a secure channel 128 | 129 | 130 | // if user switches language 131 | } else if (userPath.equals("/chooseLanguage")) { 132 | 133 | // get language choice 134 | String language = request.getParameter("language"); 135 | 136 | // place in request scope 137 | request.setAttribute("language", language); 138 | 139 | String userView = (String) session.getAttribute("view"); 140 | 141 | if ((userView != null) && 142 | (!userView.equals("/index"))) { // index.jsp exists outside 'view' folder 143 | // so must be forwarded separately 144 | userPath = userView; 145 | } else { 146 | 147 | // if previous view is index or cannot be determined, send user to welcome page 148 | try { 149 | request.getRequestDispatcher("/index.jsp").forward(request, response); 150 | } catch (Exception ex) { 151 | ex.printStackTrace(); 152 | } 153 | return; 154 | } 155 | } 156 | 157 | // use RequestDispatcher to forward request internally 158 | String url = "/WEB-INF/view" + userPath + ".jsp"; 159 | 160 | try { 161 | request.getRequestDispatcher(url).forward(request, response); 162 | } catch (Exception ex) { 163 | ex.printStackTrace(); 164 | } 165 | } 166 | 167 | 168 | /** 169 | * Handles the HTTP POST method. 170 | * @param request servlet request 171 | * @param response servlet response 172 | * @throws ServletException if a servlet-specific error occurs 173 | * @throws IOException if an I/O error occurs 174 | */ 175 | @Override 176 | protected void doPost(HttpServletRequest request, HttpServletResponse response) 177 | throws ServletException, IOException { 178 | 179 | request.setCharacterEncoding("UTF-8"); // ensures that user input is interpreted as 180 | // 8-bit Unicode (e.g., for Czech characters) 181 | 182 | String userPath = request.getServletPath(); 183 | HttpSession session = request.getSession(); 184 | ShoppingCart cart = (ShoppingCart) session.getAttribute("cart"); 185 | Validator validator = new Validator(); 186 | 187 | 188 | // if addToCart action is called 189 | if (userPath.equals("/addToCart")) { 190 | 191 | // if user is adding item to cart for first time 192 | // create cart object and attach it to user session 193 | if (cart == null) { 194 | 195 | cart = new ShoppingCart(); 196 | session.setAttribute("cart", cart); 197 | } 198 | 199 | // get user input from request 200 | String productId = request.getParameter("productId"); 201 | 202 | if (!productId.isEmpty()) { 203 | 204 | Product product = productFacade.find(Integer.parseInt(productId)); 205 | cart.addItem(product); 206 | } 207 | 208 | userPath = "/category"; 209 | 210 | 211 | // if updateCart action is called 212 | } else if (userPath.equals("/updateCart")) { 213 | 214 | // get input from request 215 | String productId = request.getParameter("productId"); 216 | String quantity = request.getParameter("quantity"); 217 | 218 | boolean invalidEntry = validator.validateQuantity(productId, quantity); 219 | 220 | if (!invalidEntry) { 221 | 222 | Product product = productFacade.find(Integer.parseInt(productId)); 223 | cart.update(product, quantity); 224 | } 225 | 226 | userPath = "/cart"; 227 | 228 | 229 | // if purchase action is called 230 | } else if (userPath.equals("/purchase")) { 231 | 232 | if (cart != null) { 233 | 234 | // extract user data from request 235 | String name = request.getParameter("name"); 236 | String email = request.getParameter("email"); 237 | String phone = request.getParameter("phone"); 238 | String address = request.getParameter("address"); 239 | String cityRegion = request.getParameter("cityRegion"); 240 | String ccNumber = request.getParameter("creditcard"); 241 | 242 | // validate user data 243 | boolean validationErrorFlag = false; 244 | validationErrorFlag = validator.validateForm(name, email, phone, address, cityRegion, ccNumber, request); 245 | 246 | // if validation error found, return user to checkout 247 | if (validationErrorFlag == true) { 248 | request.setAttribute("validationErrorFlag", validationErrorFlag); 249 | userPath = "/checkout"; 250 | 251 | // otherwise, save order to database 252 | } else { 253 | 254 | int orderId = orderManager.placeOrder(name, email, phone, address, cityRegion, ccNumber, cart); 255 | 256 | // if order processed successfully send user to confirmation page 257 | if (orderId != 0) { 258 | 259 | // in case language was set using toggle, get language choice before destroying session 260 | Locale locale = (Locale) session.getAttribute("javax.servlet.jsp.jstl.fmt.locale.session"); 261 | String language = ""; 262 | 263 | if (locale != null) { 264 | 265 | language = (String) locale.getLanguage(); 266 | } 267 | 268 | // dissociate shopping cart from session 269 | cart = null; 270 | 271 | // end session 272 | session.invalidate(); 273 | 274 | if (!language.isEmpty()) { // if user changed language using the toggle, 275 | // reset the language attribute - otherwise 276 | request.setAttribute("language", language); // language will be switched on confirmation page! 277 | } 278 | 279 | // get order details 280 | Map orderMap = orderManager.getOrderDetails(orderId); 281 | 282 | // place order details in request scope 283 | request.setAttribute("customer", orderMap.get("customer")); 284 | request.setAttribute("products", orderMap.get("products")); 285 | request.setAttribute("orderRecord", orderMap.get("orderRecord")); 286 | request.setAttribute("orderedProducts", orderMap.get("orderedProducts")); 287 | 288 | userPath = "/confirmation"; 289 | 290 | // otherwise, send back to checkout page and display error 291 | } else { 292 | userPath = "/checkout"; 293 | request.setAttribute("orderFailureFlag", true); 294 | } 295 | } 296 | } 297 | } 298 | 299 | // use RequestDispatcher to forward request internally 300 | String url = "/WEB-INF/view" + userPath + ".jsp"; 301 | 302 | try { 303 | request.getRequestDispatcher(url).forward(request, response); 304 | } catch (Exception ex) { 305 | ex.printStackTrace(); 306 | } 307 | } 308 | 309 | } -------------------------------------------------------------------------------- /src/main/java/entity/Category.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package entity; 10 | 11 | import java.io.Serializable; 12 | import java.util.Collection; 13 | import javax.persistence.Basic; 14 | import javax.persistence.CascadeType; 15 | import javax.persistence.Column; 16 | import javax.persistence.Entity; 17 | import javax.persistence.GeneratedValue; 18 | import javax.persistence.GenerationType; 19 | import javax.persistence.Id; 20 | import javax.persistence.NamedQueries; 21 | import javax.persistence.NamedQuery; 22 | import javax.persistence.OneToMany; 23 | import javax.persistence.Table; 24 | import javax.xml.bind.annotation.XmlAccessType; 25 | import javax.xml.bind.annotation.XmlAccessorType; 26 | import javax.xml.bind.annotation.XmlRootElement; 27 | import javax.xml.bind.annotation.XmlTransient; 28 | 29 | /** 30 | * 31 | * @author tgiunipero 32 | */ 33 | @Entity 34 | @Table(name = "category") 35 | @NamedQueries({ 36 | @NamedQuery(name = "Category.findAll", query = "SELECT c FROM Category c"), 37 | @NamedQuery(name = "Category.findById", query = "SELECT c FROM Category c WHERE c.id = :id"), 38 | @NamedQuery(name = "Category.findByName", query = "SELECT c FROM Category c WHERE c.name = :name")}) 39 | //@XmlRootElement 40 | //@XmlAccessorType(XmlAccessType.FIELD) 41 | public class Category implements Serializable { 42 | private static final long serialVersionUID = 1L; 43 | @Id 44 | @GeneratedValue(strategy = GenerationType.IDENTITY) 45 | @Basic(optional = false) 46 | @Column(name = "id") 47 | private Short id; 48 | @Basic(optional = false) 49 | @Column(name = "name") 50 | private String name; 51 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "category") 52 | // @XmlTransient 53 | // //@JsonIgnore 54 | private Collection productCollection; 55 | 56 | public Category() { 57 | } 58 | 59 | public Category(Short id) { 60 | this.id = id; 61 | } 62 | 63 | public Category(Short id, String name) { 64 | this.id = id; 65 | this.name = name; 66 | } 67 | 68 | public Short getId() { 69 | return id; 70 | } 71 | 72 | public void setId(Short id) { 73 | this.id = id; 74 | } 75 | 76 | public String getName() { 77 | return name; 78 | } 79 | 80 | public void setName(String name) { 81 | this.name = name; 82 | } 83 | 84 | public Collection getProductCollection() { 85 | return productCollection; 86 | } 87 | 88 | public void setProductCollection(Collection productCollection) { 89 | this.productCollection = productCollection; 90 | } 91 | 92 | @Override 93 | public int hashCode() { 94 | int hash = 0; 95 | hash += (id != null ? id.hashCode() : 0); 96 | return hash; 97 | } 98 | 99 | @Override 100 | public boolean equals(Object object) { 101 | // TODO: Warning - this method won't work in the case the id fields are not set 102 | if (!(object instanceof Category)) { 103 | return false; 104 | } 105 | Category other = (Category) object; 106 | if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { 107 | return false; 108 | } 109 | return true; 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | return "entity.Category[id=" + id + "]"; 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /src/main/java/entity/Customer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package entity; 10 | 11 | import java.io.Serializable; 12 | import java.util.Collection; 13 | import javax.persistence.Basic; 14 | import javax.persistence.CascadeType; 15 | import javax.persistence.Column; 16 | import javax.persistence.Entity; 17 | import javax.persistence.GeneratedValue; 18 | import javax.persistence.GenerationType; 19 | import javax.persistence.Id; 20 | import javax.persistence.NamedQueries; 21 | import javax.persistence.NamedQuery; 22 | import javax.persistence.OneToMany; 23 | import javax.persistence.Table; 24 | 25 | /** 26 | * 27 | * @author tgiunipero 28 | */ 29 | @Entity 30 | @Table(name = "customer") 31 | @NamedQueries({ 32 | @NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c"), 33 | @NamedQuery(name = "Customer.findById", query = "SELECT c FROM Customer c WHERE c.id = :id"), 34 | @NamedQuery(name = "Customer.findByName", query = "SELECT c FROM Customer c WHERE c.name = :name"), 35 | @NamedQuery(name = "Customer.findByEmail", query = "SELECT c FROM Customer c WHERE c.email = :email"), 36 | @NamedQuery(name = "Customer.findByPhone", query = "SELECT c FROM Customer c WHERE c.phone = :phone"), 37 | @NamedQuery(name = "Customer.findByAddress", query = "SELECT c FROM Customer c WHERE c.address = :address"), 38 | @NamedQuery(name = "Customer.findByCityRegion", query = "SELECT c FROM Customer c WHERE c.cityRegion = :cityRegion"), 39 | @NamedQuery(name = "Customer.findByCcNumber", query = "SELECT c FROM Customer c WHERE c.ccNumber = :ccNumber")}) 40 | public class Customer implements Serializable { 41 | private static final long serialVersionUID = 1L; 42 | @Id 43 | @GeneratedValue(strategy = GenerationType.IDENTITY) 44 | @Basic(optional = false) 45 | @Column(name = "id") 46 | private Integer id; 47 | @Basic(optional = false) 48 | @Column(name = "name") 49 | private String name; 50 | @Basic(optional = false) 51 | @Column(name = "email") 52 | private String email; 53 | @Basic(optional = false) 54 | @Column(name = "phone") 55 | private String phone; 56 | @Basic(optional = false) 57 | @Column(name = "address") 58 | private String address; 59 | @Basic(optional = false) 60 | @Column(name = "city_region") 61 | private String cityRegion; 62 | @Basic(optional = false) 63 | @Column(name = "cc_number") 64 | private String ccNumber; 65 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "customer") 66 | private Collection customerOrderCollection; 67 | 68 | public Customer() { 69 | } 70 | 71 | public Customer(Integer id) { 72 | this.id = id; 73 | } 74 | 75 | public Customer(Integer id, String name, String email, String phone, String address, String cityRegion, String ccNumber) { 76 | this.id = id; 77 | this.name = name; 78 | this.email = email; 79 | this.phone = phone; 80 | this.address = address; 81 | this.cityRegion = cityRegion; 82 | this.ccNumber = ccNumber; 83 | } 84 | 85 | public Integer getId() { 86 | return id; 87 | } 88 | 89 | public void setId(Integer id) { 90 | this.id = id; 91 | } 92 | 93 | public String getName() { 94 | return name; 95 | } 96 | 97 | public void setName(String name) { 98 | this.name = name; 99 | } 100 | 101 | public String getEmail() { 102 | return email; 103 | } 104 | 105 | public void setEmail(String email) { 106 | this.email = email; 107 | } 108 | 109 | public String getPhone() { 110 | return phone; 111 | } 112 | 113 | public void setPhone(String phone) { 114 | this.phone = phone; 115 | } 116 | 117 | public String getAddress() { 118 | return address; 119 | } 120 | 121 | public void setAddress(String address) { 122 | this.address = address; 123 | } 124 | 125 | public String getCityRegion() { 126 | return cityRegion; 127 | } 128 | 129 | public void setCityRegion(String cityRegion) { 130 | this.cityRegion = cityRegion; 131 | } 132 | 133 | public String getCcNumber() { 134 | return ccNumber; 135 | } 136 | 137 | public void setCcNumber(String ccNumber) { 138 | this.ccNumber = ccNumber; 139 | } 140 | 141 | public Collection getCustomerOrderCollection() { 142 | return customerOrderCollection; 143 | } 144 | 145 | public void setCustomerOrderCollection(Collection customerOrderCollection) { 146 | this.customerOrderCollection = customerOrderCollection; 147 | } 148 | 149 | @Override 150 | public int hashCode() { 151 | int hash = 0; 152 | hash += (id != null ? id.hashCode() : 0); 153 | return hash; 154 | } 155 | 156 | @Override 157 | public boolean equals(Object object) { 158 | // TODO: Warning - this method won't work in the case the id fields are not set 159 | if (!(object instanceof Customer)) { 160 | return false; 161 | } 162 | Customer other = (Customer) object; 163 | if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { 164 | return false; 165 | } 166 | return true; 167 | } 168 | 169 | @Override 170 | public String toString() { 171 | return "entity.Customer[id=" + id + "]"; 172 | } 173 | 174 | } -------------------------------------------------------------------------------- /src/main/java/entity/CustomerOrder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package entity; 10 | 11 | import java.io.Serializable; 12 | import java.math.BigDecimal; 13 | import java.util.Collection; 14 | import java.util.Date; 15 | import javax.persistence.Basic; 16 | import javax.persistence.CascadeType; 17 | import javax.persistence.Column; 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.GenerationType; 21 | import javax.persistence.Id; 22 | import javax.persistence.JoinColumn; 23 | import javax.persistence.ManyToOne; 24 | import javax.persistence.NamedQueries; 25 | import javax.persistence.NamedQuery; 26 | import javax.persistence.OneToMany; 27 | import javax.persistence.Table; 28 | import javax.persistence.Temporal; 29 | import javax.persistence.TemporalType; 30 | 31 | /** 32 | * 33 | * @author tgiunipero 34 | */ 35 | @Entity 36 | @Table(name = "customer_order") 37 | @NamedQueries({ 38 | @NamedQuery(name = "CustomerOrder.findAll", query = "SELECT c FROM CustomerOrder c"), 39 | @NamedQuery(name = "CustomerOrder.findById", query = "SELECT c FROM CustomerOrder c WHERE c.id = :id"), 40 | @NamedQuery(name = "CustomerOrder.findByCustomer", query = "SELECT c FROM CustomerOrder c WHERE c.customer = :customer"), // manually created 41 | @NamedQuery(name = "CustomerOrder.findByAmount", query = "SELECT c FROM CustomerOrder c WHERE c.amount = :amount"), 42 | @NamedQuery(name = "CustomerOrder.findByDateCreated", query = "SELECT c FROM CustomerOrder c WHERE c.dateCreated = :dateCreated"), 43 | @NamedQuery(name = "CustomerOrder.findByConfirmationNumber", query = "SELECT c FROM CustomerOrder c WHERE c.confirmationNumber = :confirmationNumber")}) 44 | public class CustomerOrder implements Serializable { 45 | private static final long serialVersionUID = 1L; 46 | @Id 47 | @GeneratedValue(strategy = GenerationType.IDENTITY) 48 | @Basic(optional = false) 49 | @Column(name = "id") 50 | private Integer id; 51 | @Basic(optional = false) 52 | @Column(name = "amount") 53 | private BigDecimal amount; 54 | @Basic(optional = false) 55 | @Column(name = "date_created") 56 | @Temporal(TemporalType.TIMESTAMP) 57 | private Date dateCreated; 58 | @Basic(optional = false) 59 | @Column(name = "confirmation_number") 60 | private int confirmationNumber; 61 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "customerOrder") 62 | private Collection orderedProductCollection; 63 | @JoinColumn(name = "customer_id", referencedColumnName = "id") 64 | @ManyToOne(optional = false) 65 | private Customer customer; 66 | 67 | public CustomerOrder() { 68 | } 69 | 70 | public CustomerOrder(Integer id) { 71 | this.id = id; 72 | } 73 | 74 | public CustomerOrder(Integer id, BigDecimal amount, Date dateCreated, int confirmationNumber) { 75 | this.id = id; 76 | this.amount = amount; 77 | this.dateCreated = dateCreated; 78 | this.confirmationNumber = confirmationNumber; 79 | } 80 | 81 | public Integer getId() { 82 | return id; 83 | } 84 | 85 | public void setId(Integer id) { 86 | this.id = id; 87 | } 88 | 89 | public BigDecimal getAmount() { 90 | return amount; 91 | } 92 | 93 | public void setAmount(BigDecimal amount) { 94 | this.amount = amount; 95 | } 96 | 97 | public Date getDateCreated() { 98 | return dateCreated; 99 | } 100 | 101 | public void setDateCreated(Date dateCreated) { 102 | this.dateCreated = dateCreated; 103 | } 104 | 105 | public int getConfirmationNumber() { 106 | return confirmationNumber; 107 | } 108 | 109 | public void setConfirmationNumber(int confirmationNumber) { 110 | this.confirmationNumber = confirmationNumber; 111 | } 112 | 113 | public Collection getOrderedProductCollection() { 114 | return orderedProductCollection; 115 | } 116 | 117 | public void setOrderedProductCollection(Collection orderedProductCollection) { 118 | this.orderedProductCollection = orderedProductCollection; 119 | } 120 | 121 | public Customer getCustomer() { 122 | return customer; 123 | } 124 | 125 | public void setCustomer(Customer customer) { 126 | this.customer = customer; 127 | } 128 | 129 | @Override 130 | public int hashCode() { 131 | int hash = 0; 132 | hash += (id != null ? id.hashCode() : 0); 133 | return hash; 134 | } 135 | 136 | @Override 137 | public boolean equals(Object object) { 138 | // TODO: Warning - this method won't work in the case the id fields are not set 139 | if (!(object instanceof CustomerOrder)) { 140 | return false; 141 | } 142 | CustomerOrder other = (CustomerOrder) object; 143 | if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { 144 | return false; 145 | } 146 | return true; 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return "entity.CustomerOrder[id=" + id + "]"; 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /src/main/java/entity/OrderedProduct.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package entity; 10 | 11 | import java.io.Serializable; 12 | import javax.persistence.Basic; 13 | import javax.persistence.Column; 14 | import javax.persistence.EmbeddedId; 15 | import javax.persistence.Entity; 16 | import javax.persistence.JoinColumn; 17 | import javax.persistence.ManyToOne; 18 | import javax.persistence.NamedQueries; 19 | import javax.persistence.NamedQuery; 20 | import javax.persistence.Table; 21 | import javax.xml.bind.annotation.XmlAccessType; 22 | import javax.xml.bind.annotation.XmlAccessorType; 23 | import javax.xml.bind.annotation.XmlRootElement; 24 | import javax.xml.bind.annotation.XmlTransient; 25 | 26 | /** 27 | * 28 | * @author tgiunipero 29 | */ 30 | @Entity 31 | @Table(name = "ordered_product") 32 | @NamedQueries({ 33 | @NamedQuery(name = "OrderedProduct.findAll", query = "SELECT o FROM OrderedProduct o"), 34 | @NamedQuery(name = "OrderedProduct.findByCustomerOrderId", query = "SELECT o FROM OrderedProduct o WHERE o.orderedProductPK.customerOrderId = :customerOrderId"), 35 | @NamedQuery(name = "OrderedProduct.findByProductId", query = "SELECT o FROM OrderedProduct o WHERE o.orderedProductPK.productId = :productId"), 36 | @NamedQuery(name = "OrderedProduct.findByQuantity", query = "SELECT o FROM OrderedProduct o WHERE o.quantity = :quantity")}) 37 | //@XmlRootElement 38 | //@XmlAccessorType(XmlAccessType.FIELD) 39 | public class OrderedProduct implements Serializable { 40 | private static final long serialVersionUID = 1L; 41 | @EmbeddedId 42 | protected OrderedProductPK orderedProductPK; 43 | @Basic(optional = false) 44 | @Column(name = "quantity") 45 | private short quantity; 46 | // @XmlTransient 47 | // //@JsonIgnore 48 | @JoinColumn(name = "product_id", referencedColumnName = "id", insertable = false, updatable = false) 49 | @ManyToOne(optional = false) 50 | private Product product; 51 | // @XmlTransient 52 | // //@JsonIgnore 53 | @JoinColumn(name = "customer_order_id", referencedColumnName = "id", insertable = false, updatable = false) 54 | @ManyToOne(optional = false) 55 | private CustomerOrder customerOrder; 56 | 57 | public OrderedProduct() { 58 | } 59 | 60 | public OrderedProduct(OrderedProductPK orderedProductPK) { 61 | this.orderedProductPK = orderedProductPK; 62 | } 63 | 64 | public OrderedProduct(OrderedProductPK orderedProductPK, short quantity) { 65 | this.orderedProductPK = orderedProductPK; 66 | this.quantity = quantity; 67 | } 68 | 69 | public OrderedProduct(int customerOrderId, int productId) { 70 | this.orderedProductPK = new OrderedProductPK(customerOrderId, productId); 71 | } 72 | 73 | public OrderedProductPK getOrderedProductPK() { 74 | return orderedProductPK; 75 | } 76 | 77 | public void setOrderedProductPK(OrderedProductPK orderedProductPK) { 78 | this.orderedProductPK = orderedProductPK; 79 | } 80 | 81 | public short getQuantity() { 82 | return quantity; 83 | } 84 | 85 | public void setQuantity(short quantity) { 86 | this.quantity = quantity; 87 | } 88 | 89 | public Product getProduct() { 90 | return product; 91 | } 92 | 93 | public void setProduct(Product product) { 94 | this.product = product; 95 | } 96 | 97 | public CustomerOrder getCustomerOrder() { 98 | return customerOrder; 99 | } 100 | 101 | public void setCustomerOrder(CustomerOrder customerOrder) { 102 | this.customerOrder = customerOrder; 103 | } 104 | 105 | @Override 106 | public int hashCode() { 107 | int hash = 0; 108 | hash += (orderedProductPK != null ? orderedProductPK.hashCode() : 0); 109 | return hash; 110 | } 111 | 112 | @Override 113 | public boolean equals(Object object) { 114 | // TODO: Warning - this method won't work in the case the id fields are not set 115 | if (!(object instanceof OrderedProduct)) { 116 | return false; 117 | } 118 | OrderedProduct other = (OrderedProduct) object; 119 | if ((this.orderedProductPK == null && other.orderedProductPK != null) || (this.orderedProductPK != null && !this.orderedProductPK.equals(other.orderedProductPK))) { 120 | return false; 121 | } 122 | return true; 123 | } 124 | 125 | @Override 126 | public String toString() { 127 | return "entity.OrderedProduct[orderedProductPK=" + orderedProductPK + "]"; 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /src/main/java/entity/OrderedProductPK.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package entity; 10 | 11 | import java.io.Serializable; 12 | import javax.persistence.Basic; 13 | import javax.persistence.Column; 14 | import javax.persistence.Embeddable; 15 | 16 | /** 17 | * 18 | * @author tgiunipero 19 | */ 20 | @Embeddable 21 | public class OrderedProductPK implements Serializable { 22 | @Basic(optional = false) 23 | @Column(name = "customer_order_id") 24 | private int customerOrderId; 25 | @Basic(optional = false) 26 | @Column(name = "product_id") 27 | private int productId; 28 | 29 | public OrderedProductPK() { 30 | } 31 | 32 | public OrderedProductPK(int customerOrderId, int productId) { 33 | this.customerOrderId = customerOrderId; 34 | this.productId = productId; 35 | } 36 | 37 | public int getCustomerOrderId() { 38 | return customerOrderId; 39 | } 40 | 41 | public void setCustomerOrderId(int customerOrderId) { 42 | this.customerOrderId = customerOrderId; 43 | } 44 | 45 | public int getProductId() { 46 | return productId; 47 | } 48 | 49 | public void setProductId(int productId) { 50 | this.productId = productId; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | int hash = 0; 56 | hash += (int) customerOrderId; 57 | hash += (int) productId; 58 | return hash; 59 | } 60 | 61 | @Override 62 | public boolean equals(Object object) { 63 | // TODO: Warning - this method won't work in the case the id fields are not set 64 | if (!(object instanceof OrderedProductPK)) { 65 | return false; 66 | } 67 | OrderedProductPK other = (OrderedProductPK) object; 68 | if (this.customerOrderId != other.customerOrderId) { 69 | return false; 70 | } 71 | if (this.productId != other.productId) { 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return "entity.OrderedProductPK[customerOrderId=" + customerOrderId + ", productId=" + productId + "]"; 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/main/java/entity/Product.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package entity; 10 | 11 | import java.io.Serializable; 12 | import java.math.BigDecimal; 13 | import java.util.Collection; 14 | import java.util.Date; 15 | import javax.persistence.Basic; 16 | import javax.persistence.CascadeType; 17 | import javax.persistence.Column; 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.GenerationType; 21 | import javax.persistence.Id; 22 | import javax.persistence.JoinColumn; 23 | import javax.persistence.ManyToOne; 24 | import javax.persistence.NamedQueries; 25 | import javax.persistence.NamedQuery; 26 | import javax.persistence.OneToMany; 27 | import javax.persistence.Table; 28 | import javax.persistence.Temporal; 29 | import javax.persistence.TemporalType; 30 | import javax.xml.bind.annotation.XmlAccessType; 31 | import javax.xml.bind.annotation.XmlAccessorType; 32 | import javax.xml.bind.annotation.XmlRootElement; 33 | import javax.xml.bind.annotation.XmlTransient; 34 | 35 | /** 36 | * 37 | * @author tgiunipero 38 | */ 39 | @Entity 40 | @Table(name = "product") 41 | @NamedQueries({ 42 | @NamedQuery(name = "Product.findAll", query = "SELECT p FROM Product p"), 43 | @NamedQuery(name = "Product.findById", query = "SELECT p FROM Product p WHERE p.id = :id"), 44 | @NamedQuery(name = "Product.findByName", query = "SELECT p FROM Product p WHERE p.name = :name"), 45 | @NamedQuery(name = "Product.findByPrice", query = "SELECT p FROM Product p WHERE p.price = :price"), 46 | @NamedQuery(name = "Product.findByLastUpdate", query = "SELECT p FROM Product p WHERE p.lastUpdate = :lastUpdate")}) 47 | //@XmlRootElement 48 | //@XmlAccessorType(XmlAccessType.FIELD) 49 | public class Product implements Serializable { 50 | private static final long serialVersionUID = 1L; 51 | @Id 52 | @GeneratedValue(strategy = GenerationType.IDENTITY) 53 | @Basic(optional = false) 54 | @Column(name = "id") 55 | private Integer id; 56 | @Basic(optional = false) 57 | @Column(name = "name") 58 | private String name; 59 | @Basic(optional = false) 60 | @Column(name = "price", precision = 15, scale = 2) 61 | private BigDecimal price; 62 | @Basic(optional = false) 63 | @Column(name = "last_update") 64 | @Temporal(TemporalType.TIMESTAMP) 65 | private Date lastUpdate; 66 | @JoinColumn(name = "category_id", referencedColumnName = "id") 67 | @ManyToOne(optional = false) 68 | private Category category; 69 | // @XmlTransient 70 | // //@JsonIgnore 71 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "product") 72 | private Collection orderedProductCollection; 73 | 74 | public Product() { 75 | } 76 | 77 | public Product(Integer id) { 78 | this.id = id; 79 | } 80 | 81 | public Product(Integer id, String name, BigDecimal price, Date lastUpdate) { 82 | this.id = id; 83 | this.name = name; 84 | this.price = price; 85 | this.lastUpdate = lastUpdate; 86 | } 87 | 88 | public Integer getId() { 89 | return id; 90 | } 91 | 92 | public void setId(Integer id) { 93 | this.id = id; 94 | } 95 | 96 | public String getName() { 97 | return name; 98 | } 99 | 100 | public void setName(String name) { 101 | this.name = name; 102 | } 103 | 104 | public BigDecimal getPrice() { 105 | return price; 106 | } 107 | 108 | public void setPrice(BigDecimal price) { 109 | this.price = price; 110 | } 111 | 112 | public Date getLastUpdate() { 113 | return lastUpdate; 114 | } 115 | 116 | public void setLastUpdate(Date lastUpdate) { 117 | this.lastUpdate = lastUpdate; 118 | } 119 | 120 | public Category getCategory() { 121 | return category; 122 | } 123 | 124 | public void setCategory(Category category) { 125 | this.category = category; 126 | } 127 | 128 | public Collection getOrderedProductCollection() { 129 | return orderedProductCollection; 130 | } 131 | 132 | public void setOrderedProductCollection(Collection orderedProductCollection) { 133 | this.orderedProductCollection = orderedProductCollection; 134 | } 135 | 136 | @Override 137 | public int hashCode() { 138 | int hash = 0; 139 | hash += (id != null ? id.hashCode() : 0); 140 | return hash; 141 | } 142 | 143 | @Override 144 | public boolean equals(Object object) { 145 | // TODO: Warning - this method won't work in the case the id fields are not set 146 | if (!(object instanceof Product)) { 147 | return false; 148 | } 149 | Product other = (Product) object; 150 | if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { 151 | return false; 152 | } 153 | return true; 154 | } 155 | 156 | @Override 157 | public String toString() { 158 | return "entity.Product[id=" + id + "]"; 159 | } 160 | 161 | } -------------------------------------------------------------------------------- /src/main/java/entity/dbschema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE product (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, last_update TIMESTAMP, name VARCHAR(255), price DECIMAL(15,2), category_id SMALLINT, PRIMARY KEY (id)); 2 | CREATE TABLE customer (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, address VARCHAR(255), cc_number VARCHAR(255), city_region VARCHAR(255), email VARCHAR(255), name VARCHAR(255), phone VARCHAR(255), PRIMARY KEY (id)); 3 | CREATE TABLE category (id SMALLINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255), PRIMARY KEY (id)); 4 | CREATE TABLE customer_order (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, amount DECIMAL(15), confirmation_number INTEGER, date_created TIMESTAMP, customer_id INTEGER, PRIMARY KEY (id)); 5 | CREATE TABLE ordered_product (quantity SMALLINT, customer_order_id INTEGER NOT NULL, product_id INTEGER NOT NULL, PRIMARY KEY (customer_order_id, product_id)); 6 | ALTER TABLE product ADD CONSTRAINT productcategory_id FOREIGN KEY (category_id) REFERENCES category (id); 7 | ALTER TABLE customer_order ADD CONSTRAINT cstmrordercstmerid FOREIGN KEY (customer_id) REFERENCES customer (id); 8 | ALTER TABLE ordered_product ADD CONSTRAINT rdrdprdctcstmrrdrd FOREIGN KEY (customer_order_id) REFERENCES customer_order (id); 9 | ALTER TABLE ordered_product ADD CONSTRAINT rdrdproductprdctid FOREIGN KEY (product_id) REFERENCES product (id); 10 | -------------------------------------------------------------------------------- /src/main/java/filter/SessionTimeoutFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package filter; 10 | 11 | import java.io.IOException; 12 | import javax.servlet.Filter; 13 | import javax.servlet.FilterChain; 14 | import javax.servlet.FilterConfig; 15 | import javax.servlet.ServletException; 16 | import javax.servlet.ServletRequest; 17 | import javax.servlet.ServletResponse; 18 | import javax.servlet.annotation.WebFilter; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpSession; 21 | 22 | 23 | /** 24 | * 25 | * @author tgiunipero 26 | */ 27 | @WebFilter(servletNames = {"Controller"}) 28 | public class SessionTimeoutFilter implements Filter { 29 | 30 | @Override 31 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 32 | throws IOException, ServletException { 33 | 34 | HttpServletRequest req = (HttpServletRequest) request; 35 | 36 | HttpSession session = req.getSession(false); 37 | 38 | // if session doesn't exist, forward user to welcome page 39 | if (session == null) { 40 | try { 41 | req.getRequestDispatcher("/index.jsp").forward(request, response); 42 | } catch (Exception ex) { 43 | ex.printStackTrace(); 44 | } 45 | return; 46 | } 47 | 48 | chain.doFilter(request, response); 49 | } 50 | 51 | @Override 52 | public void init(FilterConfig filterConfig) throws ServletException {} 53 | 54 | @Override 55 | public void destroy() {} 56 | 57 | } -------------------------------------------------------------------------------- /src/main/java/resources/messages.properties: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | # 4 | # You may not modify, use, reproduce, or distribute this software 5 | # except in compliance with the terms of the license at: 6 | # http://developer.sun.com/berkeley_license.html 7 | 8 | # author: tgiunipero 9 | 10 | # Default (English) language resource bundle for the AffableBean application 11 | 12 | # welcome page 13 | greeting=Welcome to the online home of the Affable Bean Green Grocer. 14 | introText=Our unique home delivery service brings you fresh organic produce, dairy, meats, breads and other delicious and healthy items direct to your doorstep. 15 | 16 | # categories 17 | dairy=dairy 18 | meats=meats 19 | bakery=bakery 20 | fruit\ &\ veg=fruit & veg 21 | 22 | # products 23 | milk=milk 24 | cheese=cheese 25 | butter=butter 26 | free\ range\ eggs=free range eggs 27 | 28 | organic\ meat\ patties=organic meat patties 29 | parma\ ham=parma ham 30 | chicken\ leg=chicken leg 31 | sausages=sausages 32 | 33 | sunflower\ seed\ loaf=sunflower seed loaf 34 | sesame\ seed\ bagel=sesame seed bagel 35 | pumpkin\ seed\ bun=pumpkin seed bun 36 | chocolate\ cookies=chocolate cookies 37 | 38 | corn\ on\ the\ cob=corn on the cob 39 | red\ currants=red currants 40 | broccoli=broccoli 41 | seedless\ watermelon=seedless watermelon 42 | 43 | # product descriptions 44 | milkDescription=semi skimmed (1L) 45 | cheeseDescription=mild cheddar (330g) 46 | butterDescription=unsalted (250g) 47 | free\ range\ eggsDescription=medium-sized (6 eggs) 48 | 49 | organic\ meat\ pattiesDescription=rolled in fresh herbs
2 patties (250g) 50 | parma\ hamDescription=matured, organic (70g) 51 | chicken\ legDescription=free range (250g) 52 | sausagesDescription=reduced fat, pork
3 sausages (350g) 53 | 54 | sunflower\ seed\ loafDescription=600g 55 | sesame\ seed\ bagelDescription=4 bagels 56 | pumpkin\ seed\ bunDescription=4 buns 57 | chocolate\ cookiesDescription=contain peanuts
(3 cookies) 58 | 59 | corn\ on\ the\ cobDescription=2 pieces 60 | red\ currantsDescription=150g 61 | broccoliDescription=500g 62 | seedless\ watermelonDescription=250g 63 | 64 | # shopping cart 65 | addToCart=add to cart 66 | item=item 67 | items2-4=items 68 | items=items 69 | unit=unit 70 | 71 | # cart page 72 | name=name 73 | price=price 74 | product=product 75 | quantity=quantity 76 | update=update 77 | subtotal=subtotal 78 | yourCartContains=Your shopping cart contains 79 | yourCartEmpty=Your shopping cart is empty 80 | clearCart=clear cart 81 | continueShopping=continue shopping 82 | proceedCheckout=proceed to checkout ➟ 83 | 84 | # checkout page 85 | checkout=checkout 86 | checkoutText=In order to purchase the items in your shopping cart, please provide us with the following information: 87 | customerName=name 88 | email=email 89 | phone=phone 90 | address=address 91 | prague=Prague 92 | creditCard=credit card number 93 | submit=submit purchase 94 | surcharge=delivery surcharge 95 | total=total 96 | 97 | # conditions 98 | nextDayGuarantee=Next-day delivery is guaranteed 99 | deliveryFee1=A 100 | deliveryFee2=delivery surcharge is applied to all purchase orders 101 | 102 | # confirmation 103 | successMessage=Your order has been successfully processed and will be delivered within 24 hours. 104 | confirmationNumberMessage=Please keep a note of your confirmation number: 105 | contactMessage=If you have a query concerning your order, feel free to contact us. 106 | thankYouMessage=Thank you for shopping at the Affable Bean Green Grocer! 107 | orderSummary=order summary 108 | deliveryAddress=delivery address 109 | dateProcessed=date processed 110 | 111 | # header 112 | cart=view cart 113 | title=The Affable Bean Green Grocer 114 | 115 | # footer 116 | privacy=Privacy Policy 117 | contact=Contact 118 | 119 | # error 120 | forbidden=You don't have permission to access this page.

Please return to our home page. 121 | notFound=We're sorry. The page you requested is unavailable.

Please return to our home page. 122 | internalError=Oops! An internal error occurred.

Please return to our home page. 123 | orderFailureError=We were unable to process your order. Please try again! 124 | 125 | #field validation error 126 | validationErrorMessage=Please provide valid entries for the following field(s): 127 | nameError=name (e.g., Bilbo Baggins) 128 | emailError=email (e.g., b.baggins@hobbit.com) 129 | phoneError=phone (e.g., 222333444) 130 | addressError=address (e.g., Korunn\u00ed 56) 131 | cityRegionError=city region (e.g., 2) 132 | ccNumberError=credit card (e.g., 1111222233334444) -------------------------------------------------------------------------------- /src/main/java/resources/messages_cs.properties: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | # 4 | # You may not modify, use, reproduce, or distribute this software 5 | # except in compliance with the terms of the license at: 6 | # http://developer.sun.com/berkeley_license.html 7 | 8 | # author: tgiunipero 9 | 10 | # Czech language resource bundle for the AffableBean application 11 | 12 | # welcome page 13 | greeting=V\u00edtejte v\u00a0na\u0161em dom\u00e1c\u00edm on-line obchod\u011b Affable Bean Green Grocer. 14 | introText=Na\u0161e jedine\u010dn\u00e1 dod\u00e1vkov\u00e1 slu\u017eba V\u00e1m zajist\u00ed dopravu \u010derstv\u00fdch organick\u00fdch produkt\u016f, ml\u00e9\u010dn\u00fdch v\u00fdrobk\u016f, uzenin, pe\u010diva a dal\u0161\u00edch delikates a zdrav\u00fdch v\u00fdrok\u016f a\u017e ke dve\u0159\u00edm. 15 | 16 | # categories 17 | dairy=ml\u00e9\u010dn\u00e9 v\u00fdrobky 18 | meats=maso 19 | bakery=pe\u010divo 20 | fruit\ &\ veg=ovoce a zeleniny 21 | 22 | # products 23 | milk=ml\u00e9ko 24 | cheese=s\u00fdr 25 | butter=m\u00e1slo 26 | free\ range\ eggs=vejce z\u00a0voln\u00fdch pastvin 27 | 28 | organic\ meat\ patties=ko\u0159en\u011bn\u00e9 pirohy 29 | parma\ ham=pr\u0161ut 30 | chicken\ leg=ku\u0159ec\u00ed stehno 31 | sausages=klob\u00e1sy 32 | 33 | sunflower\ seed\ loaf=slune\u010dnicov\u00fd chl\u00e9b 34 | sesame\ seed\ bagel=sezamov\u00e1 bageta 35 | pumpkin\ seed\ bun=sem\u00ednkov\u00e1 d\u00fd\u0148ov\u00e1 buchta 36 | chocolate\ cookies=\u010dokol\u00e1dov\u00e9 su\u0161enky 37 | 38 | corn\ on\ the\ cob=kuku\u0159ice 39 | red\ currants=\u010derven\u00fd ryb\u00edz 40 | broccoli=brokolice 41 | seedless\ watermelon=meloun bez sem\u00ednek 42 | 43 | # product descriptions 44 | milkDescription=polotu\u010dn\u00e9 (1L) 45 | cheeseDescription=jemn\u00fd \u010dedar (330g) 46 | butterDescription=neslan\u00fd (250g) 47 | free\ range\ eggsDescription=st\u0159edn\u011b-velk\u00e9 (6 vajec) 48 | 49 | organic\ meat\ pattiesDescription=v \u010derstv\u00fdch bylink\u00e1ch
2 placky (250g) 50 | parma\ hamDescription=vyzr\u00e1l\u00e9, bio vep\u0159ov\u00e9 maso (70g) 51 | chicken\ legDescription=z voln\u00e9ho chovu (250g) 52 | sausagesDescription=s n\u00edzk\u00fdm obsahem tuku
3 vep\u0159ov\u00e9 klob\u00e1sky (350g) 53 | 54 | sunflower\ seed\ loafDescription=600g 55 | sesame\ seed\ bagelDescription=4 bagely 56 | pumpkin\ seed\ bunDescription=4 buchty 57 | chocolate\ cookiesDescription=obsahuj\u00ed ara\u0161\u00eddy
(3 su\u0161enky) 58 | 59 | corn\ on\ the\ cobDescription=2 klasy 60 | red\ currantsDescription=150g 61 | broccoliDescription=500g 62 | seedless\ watermelonDescription=250g 63 | 64 | # shopping cart 65 | addToCart=p\u0159idat do ko\u0161\u00edku 66 | item=polo\u017eka 67 | items2-4=polo\u017eky 68 | items=polo\u017eek 69 | unit=jednotku 70 | 71 | # cart page 72 | name=jm\u00e9no 73 | price=cena 74 | product=produkt 75 | quantity=mno\u017estv\u00ed 76 | update=aktualizovat 77 | subtotal=mezisou\u010det 78 | yourCartContains=V\u00e1\u0161 n\u00e1kupn\u00ed ko\u0161\u00edk obsahuje 79 | yourCartEmpty=V\u00e1\u0161 n\u00e1kupn\u00ed ko\u0161\u00edk je pr\u00e1zdn\u00fd 80 | clearCart=vysypat ko\u0161\u00edk 81 | continueShopping=pokra\u010dovat v\u00a0nakupov\u00e1n\u00ed 82 | proceedCheckout=p\u0159ej\u00edt ke objedn\u00e1vce ➟ 83 | 84 | # checkout page 85 | checkout=objedn\u00e1vka 86 | checkoutText=V\u00a0p\u0159\u00edpad\u011b, \u017ee vlo\u017e\u00edte V\u00e1\u0161 n\u00e1kup do ko\u0161\u00edku, poskytn\u011bte n\u00e1m n\u00e1sleduj\u00edc\u00ed informace: 87 | customerName=jm\u00e9no 88 | email=email 89 | phone=telefon 90 | address=adresa 91 | prague=Praha 92 | creditCard=\u010d\u00edslo kreditn\u00ed karty 93 | submit=odeslat objedn\u00e1vku 94 | surcharge=p\u0159ir\u00e1\u017eka za dovoz 95 | total=sou\u010det 96 | 97 | # conditions 98 | nextDayGuarantee=Garantujeme dod\u00e1n\u00ed n\u00e1kupu do druh\u00e9ho dne 99 | deliveryFee1=V\u0161echny objedn\u00e1vky zahrnuj\u00ed p\u0159ir\u00e1\u017eku v\u00a0hodnot\u011b 100 | deliveryFee2= 101 | 102 | # confirmation 103 | successMessage=Va\u0161e objedn\u00e1vka byla \u00fasp\u011b\u0161n\u011b zpracov\u00e1na a bude doru\u010dena do 24 hodin. 104 | confirmationNumberMessage=Pros\u00edm, zaznamen\u00e1vat va\u0161e \u010d\u00edslo potvrzen\u00ed: 105 | contactMessage=M\u00e1te-li dotaz t\u00fdkaj\u00edc\u00ed se objedn\u00e1vky, nev\u00e1hejte n\u00e1s kontaktovat. 106 | thankYouMessage=D\u011bkujeme V\u00e1m za V\u00e1\u0161 n\u00e1kup! 107 | orderSummary=p\u0159ehled objedn\u00e1vky 108 | deliveryAddress=doru\u010dovac\u00ed adresa 109 | dateProcessed=zpracovan\u00e1 v 110 | 111 | # header 112 | cart=zobrazit ko\u0161\u00edk 113 | title=The Affable Bean Green Grocer 114 | 115 | # footer 116 | privacy=Soukrom\u00ed 117 | contact=Kontakt 118 | 119 | # error 120 | forbidden=Nem\u00e1te povolen\u00fd p\u0159\u00edstup k\u00a0t\u00e9to str\u00e1nce.

Pros\u00edm, vra\u0165te se na na\u0161\u00ed \u00favodn\u00ed str\u00e1nku. 121 | notFound=Je n\u00e1m l\u00edto, po\u017eadovan\u00e1 str\u00e1nka je nedostupn\u00e1.

Pros\u00edm, vra\u0165te se na na\u0161\u00ed \u00favodn\u00ed str\u00e1nku. 122 | internalError=Z\u0159ejm\u011b do\u0161lo k\u00a0intern\u00ed chyb\u011b.

Pros\u00edm, vra\u0165te se na na\u0161\u00ed \u00favodn\u00ed str\u00e1nku. 123 | orderFailureError=Nebyli jsme schopni zpracovat va\u0161i objedn\u00e1vku. Pros\u00edm zkuste to znovu! 124 | 125 | #field validation error 126 | validationErrorMessage=Uve\u010fte, pros\u00edm, \u00fadaje platn\u00e9 pro n\u00e1sleduj\u00edc\u00ed oblast(i): 127 | nameError=jm\u00e9no (nap\u0159. Karel \u010capek) 128 | emailError=email (nap\u0159. k.capek@seznam.cz) 129 | phoneError=telefon (nap\u0159. 222333444) 130 | addressError=adresa (nap\u0159. Dlouh\u00e1 99) 131 | cityRegionError=\u010d\u00e1st m\u011bsta (nap\u0159. 1) 132 | ccNumberError=kreditn\u00ed karta (nap\u0159. 1111222233334444) -------------------------------------------------------------------------------- /src/main/java/resources/messages_en.properties: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | # 4 | # You may not modify, use, reproduce, or distribute this software 5 | # except in compliance with the terms of the license at: 6 | # http://developer.sun.com/berkeley_license.html 7 | 8 | # author: tgiunipero 9 | 10 | # English language resource bundle for the AffableBean application 11 | 12 | # welcome page 13 | greeting=Welcome to the online home of the Affable Bean Green Grocer. 14 | introText=Our unique home delivery service brings you fresh organic produce, dairy, meats, breads and other delicious and healthy items direct to your doorstep. 15 | 16 | # categories 17 | dairy=dairy 18 | meats=meats 19 | bakery=bakery 20 | fruit\ &\ veg=fruit & veg 21 | 22 | # products 23 | milk=milk 24 | cheese=cheese 25 | butter=butter 26 | free\ range\ eggs=free range eggs 27 | 28 | organic\ meat\ patties=organic meat patties 29 | parma\ ham=parma ham 30 | chicken\ leg=chicken leg 31 | sausages=sausages 32 | 33 | sunflower\ seed\ loaf=sunflower seed loaf 34 | sesame\ seed\ bagel=sesame seed bagel 35 | pumpkin\ seed\ bun=pumpkin seed bun 36 | chocolate\ cookies=chocolate cookies 37 | 38 | corn\ on\ the\ cob=corn on the cob 39 | red\ currants=red currants 40 | broccoli=broccoli 41 | seedless\ watermelon=seedless watermelon 42 | 43 | # product descriptions 44 | milkDescription=semi skimmed (1L) 45 | cheeseDescription=mild cheddar (330g) 46 | butterDescription=unsalted (250g) 47 | free\ range\ eggsDescription=medium-sized (6 eggs) 48 | 49 | organic\ meat\ pattiesDescription=rolled in fresh herbs
2 patties (250g) 50 | parma\ hamDescription=matured, organic (70g) 51 | chicken\ legDescription=free range (250g) 52 | sausagesDescription=reduced fat, pork
3 sausages (350g) 53 | 54 | sunflower\ seed\ loafDescription=600g 55 | sesame\ seed\ bagelDescription=4 bagels 56 | pumpkin\ seed\ bunDescription=4 buns 57 | chocolate\ cookiesDescription=contain peanuts
(3 cookies) 58 | 59 | corn\ on\ the\ cobDescription=2 pieces 60 | red\ currantsDescription=150g 61 | broccoliDescription=500g 62 | seedless\ watermelonDescription=250g 63 | 64 | # shopping cart 65 | addToCart=add to cart 66 | item=item 67 | items2-4=items 68 | items=items 69 | unit=unit 70 | 71 | # cart page 72 | name=name 73 | price=price 74 | product=product 75 | quantity=quantity 76 | update=update 77 | subtotal=subtotal 78 | yourCartContains=Your shopping cart contains 79 | yourCartEmpty=Your shopping cart is empty 80 | clearCart=clear cart 81 | continueShopping=continue shopping 82 | proceedCheckout=proceed to checkout ➟ 83 | 84 | # checkout page 85 | checkout=checkout 86 | checkoutText=In order to purchase the items in your shopping cart, please provide us with the following information: 87 | customerName=name 88 | email=email 89 | phone=phone 90 | address=address 91 | prague=Prague 92 | creditCard=credit card number 93 | submit=submit purchase 94 | surcharge=delivery surcharge 95 | total=total 96 | 97 | # conditions 98 | nextDayGuarantee=Next-day delivery is guaranteed 99 | deliveryFee1=A 100 | deliveryFee2=delivery surcharge is applied to all purchase orders 101 | 102 | # confirmation 103 | successMessage=Your order has been successfully processed and will be delivered within 24 hours. 104 | confirmationNumberMessage=Please keep a note of your confirmation number: 105 | contactMessage=If you have a query concerning your order, feel free to contact us. 106 | thankYouMessage=Thank you for shopping at the Affable Bean Green Grocer! 107 | orderSummary=order summary 108 | deliveryAddress=delivery address 109 | dateProcessed=date processed 110 | 111 | # header 112 | cart=view cart 113 | title=The Affable Bean Green Grocer 114 | 115 | # footer 116 | privacy=Privacy Policy 117 | contact=Contact 118 | 119 | # error 120 | forbidden=You don't have permission to access this page.

Please return to our home page. 121 | notFound=We're sorry. The page you requested is unavailable.

Please return to our home page. 122 | internalError=Oops! An internal error occurred.

Please return to our home page. 123 | orderFailureError=We were unable to process your order. Please try again! 124 | 125 | #field validation error 126 | validationErrorMessage=Please provide valid entries for the following field(s): 127 | nameError=name (e.g., Bilbo Baggins) 128 | emailError=email (e.g., b.baggins@hobbit.com) 129 | phoneError=phone (e.g., 222333444) 130 | addressError=address (e.g., Korunn\u00ed 56) 131 | cityRegionError=city region (e.g., 2) 132 | ccNumberError=credit card (e.g., 1111222233334444) -------------------------------------------------------------------------------- /src/main/java/rest/ApplicationResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package rest; 6 | 7 | import java.util.Set; 8 | import javax.ws.rs.core.Application; 9 | 10 | @javax.ws.rs.ApplicationPath("rest") 11 | public class ApplicationResource extends Application { 12 | 13 | public Set> getClasses() { 14 | return getRestResourceClasses(); 15 | } 16 | 17 | /** 18 | * Do not modify this method. It is automatically generated by NetBeans REST support. 19 | */ 20 | private Set> getRestResourceClasses() { 21 | Set> resources = new java.util.HashSet>(); 22 | resources.add(rest.DepartmentService.class); 23 | resources.add(rest.PurchaseService.class); 24 | // try { 25 | // Class jacksonProvider = Class.forName("org.codehaus.jackson.jaxrs.JacksonJsonProvider"); 26 | // resources.add(jacksonProvider); 27 | // } catch (ClassNotFoundException ex) { 28 | // java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex); 29 | // } 30 | return resources; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/rest/DepartmentService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package rest; 6 | 7 | import entity.Category; 8 | import entity.Product; 9 | import javax.ejb.EJB; 10 | import javax.ejb.Singleton; 11 | import javax.json.Json; 12 | import javax.json.JsonArray; 13 | import javax.json.JsonArrayBuilder; 14 | import javax.json.JsonObjectBuilder; 15 | import javax.ws.rs.PathParam; 16 | import javax.ws.rs.Path; 17 | import javax.ws.rs.GET; 18 | import javax.ws.rs.Produces; 19 | import session.CategoryFacade; 20 | 21 | @Singleton 22 | @Path("department") 23 | public class DepartmentService { 24 | 25 | @EJB 26 | private CategoryFacade categoryFacade; 27 | 28 | public DepartmentService() { 29 | } 30 | 31 | @GET 32 | @Path("{id}") 33 | @Produces("application/json") 34 | public JsonArray getDepartmentProducts(@PathParam("id")Short categoryId) { 35 | JsonArrayBuilder jb = Json.createArrayBuilder(); 36 | Category selectedCategory = categoryFacade.find(categoryId); 37 | if (selectedCategory != null) { 38 | for (Product p : selectedCategory.getProductCollection()) { 39 | JsonObjectBuilder jpb = Json.createObjectBuilder(). 40 | add("id", p.getId()). 41 | add("name", p.getName()). 42 | add("price", p.getPrice()); 43 | jb.add(jpb); 44 | } 45 | } 46 | return jb.build(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/rest/PurchaseService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | package rest; 9 | 10 | import cart.ShoppingCart; 11 | import entity.CustomerOrder; 12 | import entity.Product; 13 | import java.math.BigDecimal; 14 | import java.util.List; 15 | import java.util.Map; 16 | import javax.ejb.EJB; 17 | import javax.ejb.Singleton; 18 | import javax.ws.rs.Consumes; 19 | import javax.ws.rs.POST; 20 | import javax.ws.rs.Path; 21 | import javax.ws.rs.Produces; 22 | import session.OrderManager; 23 | import session.ProductFacade; 24 | 25 | @Singleton 26 | @Path("purchase") 27 | public class PurchaseService { 28 | 29 | @EJB 30 | private OrderManager orderManager; 31 | 32 | @EJB 33 | private ProductFacade productFacade; 34 | 35 | public PurchaseService() { 36 | } 37 | 38 | @POST 39 | @Consumes({"application/json"}) 40 | @Produces({"application/json"}) 41 | public OrderConfirmationJson placeOrder(OrderJson order) { 42 | int orderId = orderManager.placeOrder(order.name, order.email, 43 | order.phone, order.address, "X", order.ccNumber, convert(order.cart, order.surCharge)); 44 | Map orderMap = orderManager.getOrderDetails(orderId); 45 | return new OrderConfirmationJson(((CustomerOrder)orderMap.get("orderRecord")).getConfirmationNumber()); 46 | } 47 | 48 | private ShoppingCart convert(List products, BigDecimal surCharge) { 49 | ShoppingCart cart = new ShoppingCart(); 50 | for (ProductJson productJson : products) { 51 | Product p = productFacade.find(productJson.id); 52 | cart.addItem(p); 53 | } 54 | cart.calculateTotal(surCharge.toPlainString()); 55 | return cart; 56 | } 57 | 58 | public static class OrderJson { 59 | 60 | public String name; 61 | public String email; 62 | public String phone; 63 | public String address; 64 | public String cityRegion; 65 | public String ccNumber; 66 | public BigDecimal surCharge; 67 | public List cart; 68 | 69 | public OrderJson() { 70 | } 71 | 72 | } 73 | 74 | public static class ProductJson { 75 | public int id; 76 | public String name; 77 | public BigDecimal price; 78 | 79 | public ProductJson() { 80 | } 81 | 82 | public ProductJson(int id, String name, BigDecimal price) { 83 | this.id = id; 84 | this.name = name; 85 | this.price = price; 86 | } 87 | 88 | public ProductJson(Product p) { 89 | this(p.getId(), p.getName(), p.getPrice()); 90 | } 91 | 92 | } 93 | 94 | public static class OrderConfirmationJson { 95 | public int orderId; 96 | 97 | public OrderConfirmationJson() { 98 | } 99 | 100 | public OrderConfirmationJson(int orderId) { 101 | this.orderId = orderId; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return "OrderConfirmation{" + "orderId=" + orderId + '}'; 107 | } 108 | 109 | 110 | } 111 | } -------------------------------------------------------------------------------- /src/main/java/session/AbstractFacade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import java.util.List; 12 | import javax.persistence.EntityManager; 13 | 14 | /** 15 | * 16 | * @author tgiunipero 17 | */ 18 | public abstract class AbstractFacade { 19 | private Class entityClass; 20 | 21 | public AbstractFacade(Class entityClass) { 22 | this.entityClass = entityClass; 23 | } 24 | 25 | protected abstract EntityManager getEntityManager(); 26 | 27 | public void create(T entity) { 28 | getEntityManager().persist(entity); 29 | } 30 | 31 | public void edit(T entity) { 32 | getEntityManager().merge(entity); 33 | } 34 | 35 | public void remove(T entity) { 36 | getEntityManager().remove(getEntityManager().merge(entity)); 37 | } 38 | 39 | public T find(Object id) { 40 | return getEntityManager().find(entityClass, id); 41 | } 42 | 43 | public List findAll() { 44 | javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); 45 | cq.select(cq.from(entityClass)); 46 | return getEntityManager().createQuery(cq).getResultList(); 47 | } 48 | 49 | public List findRange(int[] range) { 50 | javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); 51 | cq.select(cq.from(entityClass)); 52 | javax.persistence.Query q = getEntityManager().createQuery(cq); 53 | q.setMaxResults(range[1] - range[0]); 54 | q.setFirstResult(range[0]); 55 | return q.getResultList(); 56 | } 57 | 58 | public int count() { 59 | javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); 60 | javax.persistence.criteria.Root rt = cq.from(entityClass); 61 | cq.select(getEntityManager().getCriteriaBuilder().count(rt)); 62 | javax.persistence.Query q = getEntityManager().createQuery(cq); 63 | return ((Long) q.getSingleResult()).intValue(); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/main/java/session/CategoryFacade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import entity.Category; 12 | import javax.ejb.Stateless; 13 | import javax.persistence.EntityManager; 14 | import javax.persistence.PersistenceContext; 15 | 16 | /** 17 | * 18 | * @author tgiunipero 19 | */ 20 | @Stateless 21 | public class CategoryFacade extends AbstractFacade { 22 | @PersistenceContext(unitName = "AffableBeanPU") 23 | private EntityManager em; 24 | 25 | protected EntityManager getEntityManager() { 26 | return em; 27 | } 28 | 29 | public CategoryFacade() { 30 | super(Category.class); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/java/session/CustomerFacade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import entity.Customer; 12 | import javax.ejb.Stateless; 13 | import javax.persistence.EntityManager; 14 | import javax.persistence.PersistenceContext; 15 | 16 | /** 17 | * 18 | * @author tgiunipero 19 | */ 20 | @Stateless 21 | public class CustomerFacade extends AbstractFacade { 22 | @PersistenceContext(unitName = "AffableBeanPU") 23 | private EntityManager em; 24 | 25 | protected EntityManager getEntityManager() { 26 | return em; 27 | } 28 | 29 | public CustomerFacade() { 30 | super(Customer.class); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/java/session/CustomerOrderFacade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import entity.CustomerOrder; 12 | import javax.annotation.security.RolesAllowed; 13 | import javax.ejb.Stateless; 14 | import javax.persistence.EntityManager; 15 | import javax.persistence.PersistenceContext; 16 | 17 | /** 18 | * 19 | * @author tgiunipero 20 | */ 21 | @Stateless 22 | public class CustomerOrderFacade extends AbstractFacade { 23 | @PersistenceContext(unitName = "AffableBeanPU") 24 | private EntityManager em; 25 | 26 | protected EntityManager getEntityManager() { 27 | return em; 28 | } 29 | 30 | public CustomerOrderFacade() { 31 | super(CustomerOrder.class); 32 | } 33 | 34 | // overridden - refresh method called to retrieve order id from database 35 | @Override 36 | public CustomerOrder find(Object id) { 37 | CustomerOrder order = em.find(CustomerOrder.class, id); 38 | em.refresh(order); 39 | return order; 40 | } 41 | 42 | public CustomerOrder findByCustomer(Object customer) { 43 | return (CustomerOrder) em.createNamedQuery("CustomerOrder.findByCustomer").setParameter("customer", customer).getSingleResult(); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/main/java/session/OrderManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import cart.*; 12 | import entity.*; 13 | import java.math.BigDecimal; 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.Random; 19 | import javax.annotation.Resource; 20 | import javax.ejb.EJB; 21 | import javax.ejb.SessionContext; 22 | import javax.ejb.Stateless; 23 | import javax.ejb.TransactionAttribute; 24 | import javax.ejb.TransactionAttributeType; 25 | import javax.ejb.TransactionManagement; 26 | import javax.ejb.TransactionManagementType; 27 | import javax.persistence.EntityManager; 28 | import javax.persistence.PersistenceContext; 29 | 30 | /** 31 | * 32 | * @author tgiunipero 33 | */ 34 | @Stateless 35 | @TransactionManagement(TransactionManagementType.CONTAINER) 36 | public class OrderManager { 37 | 38 | @PersistenceContext(unitName = "AffableBeanPU") 39 | private EntityManager em; 40 | @Resource 41 | private SessionContext context; 42 | @EJB 43 | private ProductFacade productFacade; 44 | @EJB 45 | private CustomerOrderFacade customerOrderFacade; 46 | @EJB 47 | private OrderedProductFacade orderedProductFacade; 48 | 49 | @TransactionAttribute(TransactionAttributeType.REQUIRED) 50 | public int placeOrder(String name, String email, String phone, String address, String cityRegion, String ccNumber, ShoppingCart cart) { 51 | 52 | try { 53 | Customer customer = addCustomer(name, email, phone, address, cityRegion, ccNumber); 54 | CustomerOrder order = addOrder(customer, cart); 55 | addOrderedItems(order, cart); 56 | return order.getId(); 57 | } catch (Exception e) { 58 | context.setRollbackOnly(); 59 | return 0; 60 | } 61 | } 62 | 63 | private Customer addCustomer(String name, String email, String phone, String address, String cityRegion, String ccNumber) { 64 | 65 | Customer customer = new Customer(); 66 | customer.setName(name); 67 | customer.setEmail(email); 68 | customer.setPhone(phone); 69 | customer.setAddress(address); 70 | customer.setCityRegion(cityRegion); 71 | customer.setCcNumber(ccNumber); 72 | 73 | em.persist(customer); 74 | return customer; 75 | } 76 | 77 | private CustomerOrder addOrder(Customer customer, ShoppingCart cart) { 78 | 79 | // set up customer order 80 | CustomerOrder order = new CustomerOrder(); 81 | order.setCustomer(customer); 82 | order.setAmount(BigDecimal.valueOf(cart.getTotal())); 83 | 84 | // create confirmation number 85 | Random random = new Random(); 86 | int i = random.nextInt(999999999); 87 | order.setConfirmationNumber(i); 88 | 89 | em.persist(order); 90 | return order; 91 | } 92 | 93 | private void addOrderedItems(CustomerOrder order, ShoppingCart cart) { 94 | 95 | em.flush(); 96 | 97 | List items = cart.getItems(); 98 | 99 | // iterate through shopping cart and create OrderedProducts 100 | for (ShoppingCartItem scItem : items) { 101 | 102 | int productId = scItem.getProduct().getId(); 103 | 104 | // set up primary key object 105 | OrderedProductPK orderedProductPK = new OrderedProductPK(); 106 | orderedProductPK.setCustomerOrderId(order.getId()); 107 | orderedProductPK.setProductId(productId); 108 | 109 | // create ordered item using PK object 110 | OrderedProduct orderedItem = new OrderedProduct(orderedProductPK); 111 | 112 | // set quantity 113 | orderedItem.setQuantity(scItem.getQuantity()); 114 | 115 | em.persist(orderedItem); 116 | } 117 | } 118 | 119 | public Map getOrderDetails(int orderId) { 120 | 121 | Map orderMap = new HashMap(); 122 | 123 | // get order 124 | CustomerOrder order = customerOrderFacade.find(orderId); 125 | 126 | // get customer 127 | Customer customer = order.getCustomer(); 128 | 129 | // get all ordered products 130 | List orderedProducts = orderedProductFacade.findByOrderId(orderId); 131 | 132 | // get product details for ordered items 133 | List products = new ArrayList(); 134 | 135 | for (OrderedProduct op : orderedProducts) { 136 | 137 | Product p = (Product) productFacade.find(op.getOrderedProductPK().getProductId()); 138 | products.add(p); 139 | } 140 | 141 | // add each item to orderMap 142 | orderMap.put("orderRecord", order); 143 | orderMap.put("customer", customer); 144 | orderMap.put("orderedProducts", orderedProducts); 145 | orderMap.put("products", products); 146 | 147 | return orderMap; 148 | } 149 | 150 | } -------------------------------------------------------------------------------- /src/main/java/session/OrderedProductFacade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import entity.OrderedProduct; 12 | import java.util.List; 13 | import javax.ejb.Stateless; 14 | import javax.persistence.EntityManager; 15 | import javax.persistence.PersistenceContext; 16 | 17 | /** 18 | * 19 | * @author tgiunipero 20 | */ 21 | @Stateless 22 | public class OrderedProductFacade extends AbstractFacade { 23 | @PersistenceContext(unitName = "AffableBeanPU") 24 | private EntityManager em; 25 | 26 | protected EntityManager getEntityManager() { 27 | return em; 28 | } 29 | 30 | public OrderedProductFacade() { 31 | super(OrderedProduct.class); 32 | } 33 | 34 | // manually created 35 | public List findByOrderId(Object id) { 36 | return em.createNamedQuery("OrderedProduct.findByCustomerOrderId").setParameter("customerOrderId", id).getResultList(); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/main/java/session/ProductFacade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package session; 10 | 11 | import entity.Product; 12 | import javax.ejb.Stateless; 13 | import javax.persistence.EntityManager; 14 | import javax.persistence.PersistenceContext; 15 | 16 | /** 17 | * 18 | * @author tgiunipero 19 | */ 20 | @Stateless 21 | public class ProductFacade extends AbstractFacade { 22 | @PersistenceContext(unitName = "AffableBeanPU") 23 | private EntityManager em; 24 | 25 | protected EntityManager getEntityManager() { 26 | return em; 27 | } 28 | 29 | public ProductFacade() { 30 | super(Product.class); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/java/validate/Validator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * You may not modify, use, reproduce, or distribute this software 5 | * except in compliance with the terms of the license at: 6 | * http://developer.sun.com/berkeley_license.html 7 | */ 8 | 9 | package validate; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | 13 | /** 14 | * 15 | * @author tgiunipero 16 | */ 17 | public class Validator { 18 | 19 | // ensures that quantity input is number between 0 and 99 20 | // applies to quantity fields in cart page 21 | public boolean validateQuantity (String productId, String quantity) { 22 | 23 | boolean errorFlag = false; 24 | 25 | if (!productId.isEmpty() && !quantity.isEmpty()) { 26 | 27 | int i = -1; 28 | 29 | try { 30 | 31 | i = Integer.parseInt(quantity); 32 | } catch (NumberFormatException nfe) { 33 | 34 | System.out.println("User did not enter a number in the quantity field"); 35 | } 36 | 37 | if (i < 0 || i > 99) { 38 | 39 | errorFlag = true; 40 | } 41 | } 42 | 43 | return errorFlag; 44 | } 45 | 46 | 47 | // performs simple validation on checkout form 48 | public boolean validateForm(String name, 49 | String email, 50 | String phone, 51 | String address, 52 | String cityRegion, 53 | String ccNumber, 54 | HttpServletRequest request) { 55 | 56 | boolean errorFlag = false; 57 | boolean nameError; 58 | boolean emailError; 59 | boolean phoneError; 60 | boolean addressError; 61 | boolean cityRegionError; 62 | boolean ccNumberError; 63 | 64 | if (name == null 65 | || name.equals("") 66 | || name.length() > 45) { 67 | errorFlag = true; 68 | nameError = true; 69 | request.setAttribute("nameError", nameError); 70 | } 71 | if (email == null 72 | || email.equals("") 73 | || !email.contains("@")) { 74 | errorFlag = true; 75 | emailError = true; 76 | request.setAttribute("emailError", emailError); 77 | } 78 | if (phone == null 79 | || phone.equals("") 80 | || phone.length() < 9) { 81 | errorFlag = true; 82 | phoneError = true; 83 | request.setAttribute("phoneError", phoneError); 84 | } 85 | if (address == null 86 | || address.equals("") 87 | || address.length() > 45) { 88 | errorFlag = true; 89 | addressError = true; 90 | request.setAttribute("addressError", addressError); 91 | } 92 | if (cityRegion == null 93 | || cityRegion.equals("") 94 | || cityRegion.length() > 2) { 95 | errorFlag = true; 96 | cityRegionError = true; 97 | request.setAttribute("cityRegionError", cityRegionError); 98 | } 99 | if (ccNumber == null 100 | || ccNumber.equals("") 101 | || ccNumber.length() > 19) { 102 | errorFlag = true; 103 | ccNumberError = true; 104 | request.setAttribute("ccNumberError", ccNumberError); 105 | } 106 | 107 | return errorFlag; 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.eclipse.persistence.jpa.PersistenceProvider 5 | jdbc/affablebeanderby 6 | false 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/setup/README.txt: -------------------------------------------------------------------------------- 1 | Based on the original E-commerce tutorial by Troy Giunipero, see: 2 | http://netbeans.org/kb/docs/javaee/ecommerce/setup.html 3 | 4 | The following is a course-grained summary of setup steps and requirements. 5 | 6 | The AffableBean application requires access to the JavaDB (Derby) database, which is 7 | preinstalled in the JDK, and is automatically registered in NetBeans. 8 | 9 | Before running the application, 10 | 11 | 1. From the IDE's Services window, under the Databases node right-click the 12 | Java DB node and choose Create Database. 13 | 14 | 2. In the Create Database dialog, type in 'affablebean' as the database name, 15 | 'j1' as the user name, and 'j1' as the password. 16 | 17 | 3. Click OK to exit the dialog. 18 | 19 | 4. Run the SQL scripts found in this directory. The schema.sql script creates tables 20 | necessary for the application. The sampleData.sql script adds sample data to the 21 | tables. Run the schema creation script first, then run the sample data script. 22 | To run scripts: 23 | 24 | a. Double-click each script node to open them in the IDE's editor. 25 | b. In the toolbar above the editor, make sure the connection to the 26 | 'affablebean' database is selected: 27 | 28 | jdbc:derby://localhost:1527/affablebean 29 | 30 | c. Click the Run SQL button to run the script. 31 | 32 | 33 | 34 | Notes: 35 | 36 | - The glassfish-resources.xml file creates the 'jdbc/affablebean' data source, and 'AffablebeanPool' 37 | connection pool on the server when the application is deployed. 38 | 39 | - The server may need to be restarted for the data source and connection pool settings to take 40 | effect. 41 | -------------------------------------------------------------------------------- /src/main/setup/glassfish-resources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/setup/sampleData.sql: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 2 | -- 3 | -- You may not modify, use, reproduce, or distribute this software 4 | -- except in compliance with the terms of the license at: 5 | -- http://developer.sun.com/berkeley_license.html 6 | -- 7 | -- author: tgiunipero 8 | -- 9 | 10 | -- 11 | -- Database: `affablebean` 12 | -- 13 | 14 | -- -------------------------------------------------------- 15 | 16 | -- 17 | -- Sample data for table `category` 18 | -- 19 | INSERT INTO category (name) VALUES ('dairy'),('meats'),('bakery'),('fruit & veg'); 20 | 21 | 22 | -- 23 | -- Sample data for table `product` 24 | -- 25 | --INSERT INTO product (name, price, description, category_id) VALUES ('milk', 1.70, 'semi skimmed (1L)', 1); 26 | --INSERT INTO product (name, price, description, category_id) VALUES ('cheese', 2.39, 'mild cheddar (330g)', 1); 27 | --INSERT INTO product (name, price, description, category_id) VALUES ('butter', 1.09, 'unsalted (250g)', 1); 28 | --INSERT INTO product (name, price, description, category_id) VALUES ('free range eggs', 1.76, 'medium-sized (6 eggs)', 1); 29 | INSERT INTO product (name, price, category_id) VALUES ('milk', 1.70, 1); 30 | INSERT INTO product (name, price, category_id) VALUES ('cheese', 2.39, 1); 31 | INSERT INTO product (name, price, category_id) VALUES ('butter', 1.09, 1); 32 | INSERT INTO product (name, price, category_id) VALUES ('free range eggs', 1.76, 1); 33 | 34 | INSERT INTO product (name, price, category_id) VALUES ('organic meat patties', 2.29, 2); 35 | INSERT INTO product (name, price, category_id) VALUES ('parma ham', 3.49, 2); 36 | INSERT INTO product (name, price, category_id) VALUES ('chicken leg', 2.59, 2); 37 | INSERT INTO product (name, price, category_id) VALUES ('sausages', 3.55, 2); 38 | 39 | INSERT INTO product (name, price, category_id) VALUES ('sunflower seed loaf', 1.89, 3); 40 | INSERT INTO product (name, price, category_id) VALUES ('sesame seed bagel', 1.19, 3); 41 | INSERT INTO product (name, price, category_id) VALUES ('pumpkin seed bun', 1.15, 3); 42 | INSERT INTO product (name, price, category_id) VALUES ('chocolate cookies', 2.39, 3); 43 | 44 | INSERT INTO product (name, price, category_id) VALUES ('corn on the cob', 1.59, 4); 45 | INSERT INTO product (name, price, category_id) VALUES ('red currants', 2.49, 4); 46 | INSERT INTO product (name, price, category_id) VALUES ('broccoli', 1.29, 4); 47 | INSERT INTO product (name, price, category_id) VALUES ('seedless watermelon', 1.49, 4); 48 | 49 | -------------------------------------------------------------------------------- /src/main/setup/schema.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE SCHEMA "J1"; 3 | 4 | -- ---------------------------------------------- 5 | -- DDL Statements for tables 6 | -- ---------------------------------------------- 7 | 8 | CREATE TABLE "J1"."PRODUCT" ("ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "LAST_UPDATE" TIMESTAMP, "NAME" VARCHAR(255), "PRICE" DECIMAL(15,2), "CATEGORY_ID" SMALLINT); 9 | 10 | CREATE TABLE "J1"."CATEGORY" ("ID" SMALLINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "NAME" VARCHAR(255)); 11 | 12 | CREATE TABLE "J1"."CUSTOMER" ("ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "ADDRESS" VARCHAR(255), "CC_NUMBER" VARCHAR(255), "CITY_REGION" VARCHAR(255), "EMAIL" VARCHAR(255), "NAME" VARCHAR(255), "PHONE" VARCHAR(255)); 13 | 14 | CREATE TABLE "J1"."ORDERED_PRODUCT" ("QUANTITY" SMALLINT, "CUSTOMER_ORDER_ID" INTEGER NOT NULL, "PRODUCT_ID" INTEGER NOT NULL); 15 | 16 | CREATE TABLE "J1"."CUSTOMER_ORDER" ("ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "AMOUNT" DECIMAL(15,0), "CONFIRMATION_NUMBER" INTEGER, "DATE_CREATED" TIMESTAMP, "CUSTOMER_ID" INTEGER); 17 | 18 | -- ---------------------------------------------- 19 | -- DDL Statements for keys 20 | -- ---------------------------------------------- 21 | 22 | -- PRIMARY/UNIQUE 23 | ALTER TABLE "J1"."CUSTOMER" ADD CONSTRAINT "SQL130923150446700" PRIMARY KEY ("ID"); 24 | 25 | ALTER TABLE "J1"."ORDERED_PRODUCT" ADD CONSTRAINT "SQL130923150446780" PRIMARY KEY ("CUSTOMER_ORDER_ID", "PRODUCT_ID"); 26 | 27 | ALTER TABLE "J1"."CATEGORY" ADD CONSTRAINT "SQL130923150446720" PRIMARY KEY ("ID"); 28 | 29 | ALTER TABLE "J1"."CUSTOMER_ORDER" ADD CONSTRAINT "SQL130923150446750" PRIMARY KEY ("ID"); 30 | 31 | ALTER TABLE "J1"."PRODUCT" ADD CONSTRAINT "SQL130923150446620" PRIMARY KEY ("ID"); 32 | 33 | -- FOREIGN 34 | ALTER TABLE "J1"."ORDERED_PRODUCT" ADD CONSTRAINT "RDRDPRDCTCSTMRRDRD" FOREIGN KEY ("CUSTOMER_ORDER_ID") REFERENCES "J1"."CUSTOMER_ORDER" ("ID") ON DELETE NO ACTION ON UPDATE NO ACTION; 35 | 36 | ALTER TABLE "J1"."ORDERED_PRODUCT" ADD CONSTRAINT "RDRDPRODUCTPRDCTID" FOREIGN KEY ("PRODUCT_ID") REFERENCES "J1"."PRODUCT" ("ID") ON DELETE NO ACTION ON UPDATE NO ACTION; 37 | 38 | ALTER TABLE "J1"."CUSTOMER_ORDER" ADD CONSTRAINT "CSTMRORDERCSTMERID" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "J1"."CUSTOMER" ("ID") ON DELETE NO ACTION ON UPDATE NO ACTION; 39 | 40 | ALTER TABLE "J1"."PRODUCT" ADD CONSTRAINT "PRODUCTCATEGORY_ID" FOREIGN KEY ("CATEGORY_ID") REFERENCES "J1"."CATEGORY" ("ID") ON DELETE NO ACTION ON UPDATE NO ACTION; 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/glassfish-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /AffableBean 5 | 6 | 7 | 8 | Keep a copy of the generated servlet class' java code. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | The relative path to product images 10 | productImagePath 11 | img/products/ 12 | 13 | 14 | 15 | The relative path to category images 16 | categoryImagePath 17 | img/categories/ 18 | 19 | 20 | 21 | The delivery surcharge applied to all orders 22 | deliverySurcharge 23 | 3.00 24 | 25 | 26 | 27 | 28 | 30 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/webapp/css/app.css: -------------------------------------------------------------------------------- 1 | /* app css stylesheet */ 2 | 3 | .menu { 4 | list-style: none; 5 | border-bottom: 0.1em solid black; 6 | margin-bottom: 2em; 7 | padding: 0 0 0.5em; 8 | } 9 | 10 | .menu:before { 11 | content: "["; 12 | } 13 | 14 | .menu:after { 15 | content: "]"; 16 | } 17 | 18 | .menu > li { 19 | display: inline; 20 | } 21 | 22 | .menu > li:before { 23 | content: "|"; 24 | padding-right: 0.3em; 25 | } 26 | 27 | .menu > li:nth-child(1):before { 28 | content: ""; 29 | padding: 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/webapp/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.1.0 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:auto;margin-left:0}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade.in{top:auto}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#555;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#555;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:block;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /src/main/webapp/img/cart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/cart.gif -------------------------------------------------------------------------------- /src/main/webapp/img/categories/bakery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/categories/bakery.jpg -------------------------------------------------------------------------------- /src/main/webapp/img/categories/dairy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/categories/dairy.jpg -------------------------------------------------------------------------------- /src/main/webapp/img/categories/fruitveg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/categories/fruitveg.jpg -------------------------------------------------------------------------------- /src/main/webapp/img/categories/meats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/categories/meats.jpg -------------------------------------------------------------------------------- /src/main/webapp/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/favicon.ico -------------------------------------------------------------------------------- /src/main/webapp/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/main/webapp/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/main/webapp/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/logo.png -------------------------------------------------------------------------------- /src/main/webapp/img/logoText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/logoText.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/broccoli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/broccoli.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/butter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/butter.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/cheese.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/cheese.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/chicken leg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/chicken leg.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/chocolate cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/chocolate cookies.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/corn on the cob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/corn on the cob.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/free range eggs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/free range eggs.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/milk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/milk.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/organic meat patties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/organic meat patties.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/parma ham.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/parma ham.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/pumpkin seed bun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/pumpkin seed bun.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/red currants.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/red currants.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/sausages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/sausages.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/seedless watermelon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/seedless watermelon.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/sesame seed bagel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/sesame seed bagel.png -------------------------------------------------------------------------------- /src/main/webapp/img/products/sunflower seed loaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/products/sunflower seed loaf.png -------------------------------------------------------------------------------- /src/main/webapp/img/stalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/img/stalk.png -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | The Affable Bean 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 44 | 45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/webapp/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Declare application level module which depends on services and directives 4 | angular.module('affableBeanApp', ['affableBeanApp.services', 'affableBeanApp.directives']); 5 | 6 | // configure application's routeProvider: 7 | angular.module('affableBeanApp').config(['$routeProvider', function($routeProvider) { 8 | 9 | // main page: 10 | $routeProvider.when('/main', 11 | {templateUrl: 'partials/main.html', 12 | controller: MainController}); 13 | 14 | // department page: 15 | $routeProvider.when('/department/:departmentId', 16 | {templateUrl: 'partials/department.html', 17 | controller: DepartmentController}); 18 | 19 | // shopping cart page: 20 | $routeProvider.when('/cart', 21 | {templateUrl: 'partials/cart.html', 22 | controller: CartController}); 23 | 24 | // checkout page: 25 | $routeProvider.when('/checkout', 26 | {templateUrl: 'partials/checkout.html', 27 | controller: CheckoutController}); 28 | 29 | // default fallback page: 30 | $routeProvider.otherwise({redirectTo: '/main'}); 31 | 32 | }]); 33 | -------------------------------------------------------------------------------- /src/main/webapp/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // main entry page: 4 | function MainController($scope) { 5 | } 6 | 7 | // shopping department: 8 | function DepartmentController($scope, $routeParams, Shop, Cart) { 9 | 10 | // scope init: 11 | $scope.products = Shop.query({departmentId: $routeParams.departmentId}); 12 | $scope.category = $routeParams.departmentId; 13 | 14 | // adds the product to the cart: 15 | $scope.addToCart = function(product) { 16 | Cart.add(product); 17 | }; 18 | } 19 | 20 | // shopping cart: 21 | function CartController($scope, $location, Cart) { 22 | 23 | updateScope(); 24 | 25 | // scope update: 26 | function updateScope() { 27 | $scope.cartContent = Cart.getProducts(); 28 | $scope.total = Cart.getTotal(); 29 | if (Cart.size() == 0) { 30 | $location.path( "/main" ); 31 | } 32 | } 33 | 34 | // removes the product from the cart: 35 | $scope.remove = function(product) { 36 | Cart.remove(product); 37 | updateScope(); 38 | }; 39 | 40 | // empty shopping cart: 41 | $scope.removeAll = function() { 42 | Cart.removeAll(); 43 | updateScope(); 44 | $location.path( "/category/1" ); 45 | }; 46 | } 47 | 48 | 49 | // shopping cart status: 50 | function CartStatusController($scope, Cart) { 51 | 52 | // listener on the cart content 53 | Cart.addListener(function() { 54 | updateCartContentScope(); 55 | }); 56 | 57 | updateCartContentScope(); 58 | 59 | // scope update: 60 | function updateCartContentScope() { 61 | if (Cart.size() == 0) { 62 | $scope.content = "Your cart is empty"; 63 | } else { 64 | $scope.content = Cart.size() + " items in the cart"; 65 | } 66 | } 67 | 68 | } 69 | 70 | // checkout process: 71 | function CheckoutController($scope, $location, Cart, Purchase) { 72 | 73 | // init scope: 74 | $scope.subtotal = Cart.getTotal(); 75 | $scope.surcharge = 3; 76 | $scope.total = 3 + parseFloat(Cart.getTotal()); 77 | 78 | // make purchase: 79 | $scope.submitPurchase = function(invalid) { 80 | if (invalid) { 81 | // form is not valid - show validation errors: 82 | $scope.showError = true; 83 | return; 84 | } 85 | // create new Purchase service: 86 | var newPurchase = new Purchase(); 87 | // initialize it with data: 88 | newPurchase.name = $scope.purchase.name; 89 | newPurchase.email = $scope.purchase.email; 90 | newPurchase.phone = $scope.purchase.phone; 91 | newPurchase.address = $scope.purchase.address; 92 | newPurchase.ccNumber = $scope.purchase.cc; 93 | newPurchase.cart = Cart.getProducts(); 94 | newPurchase.surCharge = 3; 95 | // and persist it: 96 | newPurchase.$save(function success(data) { 97 | // empty the cart: 98 | Cart.removeAll(); 99 | // init scope with purchase confirmation number: 100 | $scope.orderId = data.orderId; 101 | }); 102 | 103 | } 104 | 105 | // cancel purchase: 106 | $scope.cancelPurchase = function() { 107 | Cart.removeAll(); 108 | $location.path( "/main" ); 109 | } 110 | 111 | // start shopping again: 112 | $scope.startShoppingAgain = function() { 113 | $location.path( "/main" ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/webapp/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* A trick to make Boostrap DropDown menu work with AngularJS: */ 4 | angular.module('affableBeanApp.directives', []). 5 | directive('bootstrapDropdown', [function() { 6 | return function(scope, element, attrs) { 7 | jQuery('html').on('click', function () { 8 | element.removeClass('open') 9 | }) 10 | 11 | jQuery('.dropdown-toggle', element).on('click', function(e) { 12 | element.toggleClass('open'); 13 | return false; 14 | }); 15 | }; 16 | }]); 17 | -------------------------------------------------------------------------------- /src/main/webapp/js/filters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | -------------------------------------------------------------------------------- /src/main/webapp/js/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // create new module with its dependencies: 4 | angular.module('affableBeanApp.services', ['ngResource']); 5 | 6 | // and now add some services to is: 7 | 8 | // fetch products for shopping department service; offline version: 9 | //angular.module('affableBeanApp.services').factory('Shop', function($resource){ 10 | // return $resource('json/:departmentId.json', {departmentId:'@departmentId'}, { 11 | // }); 12 | //}); 13 | 14 | // fetch products for shopping department service; online version: 15 | angular.module('affableBeanApp.services').factory('Shop', function($resource){ 16 | return $resource('http://localhost\::port/AffableBean/rest/department/:departmentId', 17 | {departmentId:'@departmentId', port:8080}, {}); 18 | }); 19 | 20 | // make purchase service: 21 | angular.module('affableBeanApp.services').factory('Purchase', function($resource){ 22 | return $resource('http://localhost\::port/AffableBean/rest/purchase/', 23 | {port:8080}, {}); 24 | }); 25 | 26 | // shopping cart service: 27 | angular.module('affableBeanApp.services').factory('Cart', function(){ 28 | var shoppingCart = function () { 29 | this.items = []; 30 | this.listeners = []; 31 | this.add = function(product) { 32 | this.items.push( product); 33 | this.fireChanges(); 34 | } 35 | this.fireChanges = function() { 36 | for (var i = 0; i < this.listeners.length; i++) { 37 | this.listeners[i].call(); 38 | } 39 | } 40 | this.remove = function(product) { 41 | for (var i = 0; i < this.items.length; i++) { 42 | if (this.items[i] === product) { 43 | this.items.splice(i,1); 44 | this.fireChanges(); 45 | return; 46 | } 47 | } 48 | } 49 | this.removeAll = function() { 50 | this.items = []; 51 | this.fireChanges(); 52 | } 53 | this.size = function() { 54 | return this.items.length; 55 | } 56 | this.getProducts = function() { 57 | return this.items; 58 | } 59 | this.addListener = function(listener) { 60 | this.listeners.push( listener); 61 | } 62 | this.getTotal = function() { 63 | var total = 0; 64 | for (var i = 0; i < this.items.length; i++) { 65 | total += this.items[i].price; 66 | } 67 | return total.toFixed(2); 68 | } 69 | 70 | }; 71 | return new shoppingCart(); 72 | }); 73 | -------------------------------------------------------------------------------- /src/main/webapp/json/1.json: -------------------------------------------------------------------------------- 1 | [ { "category" : { "id" : 1, 2 | "name" : "dairy" 3 | }, 4 | "id" : 1, 5 | "lastUpdate" : 1346016098000, 6 | "name" : "milk", 7 | "orderedProductCollection" : [ ], 8 | "price" : 1.7 9 | }, 10 | { "category" : { "id" : 1, 11 | "name" : "dairy" 12 | }, 13 | "id" : 2, 14 | "lastUpdate" : 1346016098000, 15 | "name" : "cheese", 16 | "orderedProductCollection" : [ { "orderedProductPK" : { "customerOrderId" : 1, 17 | "productId" : 2 18 | }, 19 | "quantity" : 2 20 | } ], 21 | "price" : 2.3900000000000001 22 | }, 23 | { "category" : { "id" : 1, 24 | "name" : "dairy" 25 | }, 26 | "id" : 3, 27 | "lastUpdate" : 1346016098000, 28 | "name" : "butter", 29 | "orderedProductCollection" : [ ], 30 | "price" : 1.0900000000000001 31 | }, 32 | { "category" : { "id" : 1, 33 | "name" : "dairy" 34 | }, 35 | "id" : 4, 36 | "lastUpdate" : 1346016098000, 37 | "name" : "free range eggs", 38 | "orderedProductCollection" : [ ], 39 | "price" : 1.76 40 | } 41 | ] -------------------------------------------------------------------------------- /src/main/webapp/json/2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "category" : { "id" : 2, 3 | "name" : "meats" 4 | }, 5 | "id" : 5, 6 | "lastUpdate" : 1346016098000, 7 | "name" : "organic meat patties", 8 | "orderedProductCollection" : [ ], 9 | "price" : 2.29 10 | }, 11 | { "category" : { "id" : 2, 12 | "name" : "meats" 13 | }, 14 | "id" : 6, 15 | "lastUpdate" : 1346016098000, 16 | "name" : "parma ham", 17 | "orderedProductCollection" : [ ], 18 | "price" : 3.4900000000000002 19 | }, 20 | { "category" : { "id" : 2, 21 | "name" : "meats" 22 | }, 23 | "id" : 7, 24 | "lastUpdate" : 1346016098000, 25 | "name" : "chicken leg", 26 | "orderedProductCollection" : [ ], 27 | "price" : 2.5899999999999999 28 | }, 29 | { "category" : { "id" : 2, 30 | "name" : "meats" 31 | }, 32 | "id" : 8, 33 | "lastUpdate" : 1346016098000, 34 | "name" : "sausages", 35 | "orderedProductCollection" : [ ], 36 | "price" : 3.5499999999999998 37 | } 38 | ] -------------------------------------------------------------------------------- /src/main/webapp/json/3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "category" : { "id" : 3, 3 | "name" : "bakery" 4 | }, 5 | "id" : 9, 6 | "lastUpdate" : 1346016098000, 7 | "name" : "sunflower seed loaf", 8 | "orderedProductCollection" : [ ], 9 | "price" : 1.8899999999999999 10 | }, 11 | { "category" : { "id" : 3, 12 | "name" : "bakery" 13 | }, 14 | "id" : 10, 15 | "lastUpdate" : 1346016098000, 16 | "name" : "sesame seed bagel", 17 | "orderedProductCollection" : [ ], 18 | "price" : 1.1899999999999999 19 | }, 20 | { "category" : { "id" : 3, 21 | "name" : "bakery" 22 | }, 23 | "id" : 11, 24 | "lastUpdate" : 1346016098000, 25 | "name" : "pumpkin seed bun", 26 | "orderedProductCollection" : [ ], 27 | "price" : 1.1499999999999999 28 | }, 29 | { "category" : { "id" : 3, 30 | "name" : "bakery" 31 | }, 32 | "id" : 12, 33 | "lastUpdate" : 1346016098000, 34 | "name" : "chocolate cookies", 35 | "orderedProductCollection" : [ ], 36 | "price" : 2.3900000000000001 37 | } 38 | ] -------------------------------------------------------------------------------- /src/main/webapp/json/4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "category" : { "id" : 4, 3 | "name" : "fruit & veg" 4 | }, 5 | "id" : 13, 6 | "lastUpdate" : 1346016099000, 7 | "name" : "corn on the cob", 8 | "orderedProductCollection" : [ ], 9 | "price" : 1.5900000000000001 10 | }, 11 | { "category" : { "id" : 4, 12 | "name" : "fruit & veg" 13 | }, 14 | "id" : 14, 15 | "lastUpdate" : 1346016099000, 16 | "name" : "red currants", 17 | "orderedProductCollection" : [ ], 18 | "price" : 2.4900000000000002 19 | }, 20 | { "category" : { "id" : 4, 21 | "name" : "fruit & veg" 22 | }, 23 | "id" : 15, 24 | "lastUpdate" : 1346016099000, 25 | "name" : "broccoli", 26 | "orderedProductCollection" : [ ], 27 | "price" : 1.29 28 | }, 29 | { "category" : { "id" : 4, 30 | "name" : "fruit & veg" 31 | }, 32 | "id" : 16, 33 | "lastUpdate" : 1346016099000, 34 | "name" : "seedless watermelon", 35 | "orderedProductCollection" : [ ], 36 | "price" : 1.49 37 | } 38 | ] -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-cookies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.1 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) { 7 | 'use strict'; 8 | 9 | /** 10 | * @ngdoc overview 11 | * @name ngCookies 12 | */ 13 | 14 | 15 | angular.module('ngCookies', ['ng']). 16 | /** 17 | * @ngdoc object 18 | * @name ngCookies.$cookies 19 | * @requires $browser 20 | * 21 | * @description 22 | * Provides read/write access to browser's cookies. 23 | * 24 | * Only a simple Object is exposed and by adding or removing properties to/from 25 | * this object, new cookies are created/deleted at the end of current $eval. 26 | * 27 | * @example 28 | */ 29 | factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) { 30 | var cookies = {}, 31 | lastCookies = {}, 32 | lastBrowserCookies, 33 | runEval = false, 34 | copy = angular.copy, 35 | isUndefined = angular.isUndefined; 36 | 37 | //creates a poller fn that copies all cookies from the $browser to service & inits the service 38 | $browser.addPollFn(function() { 39 | var currentCookies = $browser.cookies(); 40 | if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl 41 | lastBrowserCookies = currentCookies; 42 | copy(currentCookies, lastCookies); 43 | copy(currentCookies, cookies); 44 | if (runEval) $rootScope.$apply(); 45 | } 46 | })(); 47 | 48 | runEval = true; 49 | 50 | //at the end of each eval, push cookies 51 | //TODO: this should happen before the "delayed" watches fire, because if some cookies are not 52 | // strings or browser refuses to store some cookies, we update the model in the push fn. 53 | $rootScope.$watch(push); 54 | 55 | return cookies; 56 | 57 | 58 | /** 59 | * Pushes all the cookies from the service to the browser and verifies if all cookies were stored. 60 | */ 61 | function push() { 62 | var name, 63 | value, 64 | browserCookies, 65 | updated; 66 | 67 | //delete any cookies deleted in $cookies 68 | for (name in lastCookies) { 69 | if (isUndefined(cookies[name])) { 70 | $browser.cookies(name, undefined); 71 | } 72 | } 73 | 74 | //update all cookies updated in $cookies 75 | for(name in cookies) { 76 | value = cookies[name]; 77 | if (!angular.isString(value)) { 78 | if (angular.isDefined(lastCookies[name])) { 79 | cookies[name] = lastCookies[name]; 80 | } else { 81 | delete cookies[name]; 82 | } 83 | } else if (value !== lastCookies[name]) { 84 | $browser.cookies(name, value); 85 | updated = true; 86 | } 87 | } 88 | 89 | //verify what was actually stored 90 | if (updated){ 91 | updated = false; 92 | browserCookies = $browser.cookies(); 93 | 94 | for (name in cookies) { 95 | if (cookies[name] !== browserCookies[name]) { 96 | //delete or reset all cookies that the browser dropped from $cookies 97 | if (isUndefined(browserCookies[name])) { 98 | delete cookies[name]; 99 | } else { 100 | cookies[name] = browserCookies[name]; 101 | } 102 | updated = true; 103 | } 104 | } 105 | } 106 | } 107 | }]). 108 | 109 | 110 | /** 111 | * @ngdoc object 112 | * @name ngCookies.$cookieStore 113 | * @requires $cookies 114 | * 115 | * @description 116 | * Provides a key-value (string-object) storage, that is backed by session cookies. 117 | * Objects put or retrieved from this storage are automatically serialized or 118 | * deserialized by angular's toJson/fromJson. 119 | * @example 120 | */ 121 | factory('$cookieStore', ['$cookies', function($cookies) { 122 | 123 | return { 124 | /** 125 | * @ngdoc method 126 | * @name ngCookies.$cookieStore#get 127 | * @methodOf ngCookies.$cookieStore 128 | * 129 | * @description 130 | * Returns the value of given cookie key 131 | * 132 | * @param {string} key Id to use for lookup. 133 | * @returns {Object} Deserialized cookie value. 134 | */ 135 | get: function(key) { 136 | return angular.fromJson($cookies[key]); 137 | }, 138 | 139 | /** 140 | * @ngdoc method 141 | * @name ngCookies.$cookieStore#put 142 | * @methodOf ngCookies.$cookieStore 143 | * 144 | * @description 145 | * Sets a value for given cookie key 146 | * 147 | * @param {string} key Id for the `value`. 148 | * @param {Object} value Value to be stored. 149 | */ 150 | put: function(key, value) { 151 | $cookies[key] = angular.toJson(value); 152 | }, 153 | 154 | /** 155 | * @ngdoc method 156 | * @name ngCookies.$cookieStore#remove 157 | * @methodOf ngCookies.$cookieStore 158 | * 159 | * @description 160 | * Remove given cookie 161 | * 162 | * @param {string} key Id of the key-value pair to delete. 163 | */ 164 | remove: function(key) { 165 | delete $cookies[key]; 166 | } 167 | }; 168 | 169 | }]); 170 | 171 | })(window, window.angular); 172 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-cookies.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.1 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(m,f,l){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,c){var b={},g={},h,i=!1,j=f.copy,k=f.isUndefined;c.addPollFn(function(){var a=c.cookies();h!=a&&(h=a,j(a,g),j(a,b),i&&d.$apply())})();i=!0;d.$watch(function(){var a,e,d;for(a in g)k(b[a])&&c.cookies(a,l);for(a in b)e=b[a],f.isString(e)?e!==g[a]&&(c.cookies(a,e),d=!0):f.isDefined(g[a])?b[a]=g[a]:delete b[a];if(d)for(a in e=c.cookies(),b)b[a]!==e[a]&&(k(e[a])?delete b[a]:b[a]=e[a])});return b}]).factory("$cookieStore", 7 | ["$cookies",function(d){return{get:function(c){return f.fromJson(d[c])},put:function(c,b){d[c]=f.toJson(b)},remove:function(c){delete d[c]}}}])})(window,window.angular); 8 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.1 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | 7 | ( 8 | 9 | /** 10 | * @ngdoc interface 11 | * @name angular.Module 12 | * @description 13 | * 14 | * Interface for configuring angular {@link angular.module modules}. 15 | */ 16 | 17 | function setupModuleLoader(window) { 18 | 19 | function ensure(obj, name, factory) { 20 | return obj[name] || (obj[name] = factory()); 21 | } 22 | 23 | return ensure(ensure(window, 'angular', Object), 'module', function() { 24 | /** @type {Object.} */ 25 | var modules = {}; 26 | 27 | /** 28 | * @ngdoc function 29 | * @name angular.module 30 | * @description 31 | * 32 | * The `angular.module` is a global place for creating and registering Angular modules. All 33 | * modules (angular core or 3rd party) that should be available to an application must be 34 | * registered using this mechanism. 35 | * 36 | * 37 | * # Module 38 | * 39 | * A module is a collocation of services, directives, filters, and configure information. Module 40 | * is used to configure the {@link AUTO.$injector $injector}. 41 | * 42 | *
 43 |      * // Create a new module
 44 |      * var myModule = angular.module('myModule', []);
 45 |      *
 46 |      * // register a new service
 47 |      * myModule.value('appName', 'MyCoolApp');
 48 |      *
 49 |      * // configure existing services inside initialization blocks.
 50 |      * myModule.config(function($locationProvider) {
 51 | 'use strict';
 52 |      *   // Configure existing providers
 53 |      *   $locationProvider.hashPrefix('!');
 54 |      * });
 55 |      * 
56 | * 57 | * Then you can create an injector and load your modules like this: 58 | * 59 | *
 60 |      * var injector = angular.injector(['ng', 'MyModule'])
 61 |      * 
62 | * 63 | * However it's more likely that you'll just use 64 | * {@link ng.directive:ngApp ngApp} or 65 | * {@link angular.bootstrap} to simplify this process for you. 66 | * 67 | * @param {!string} name The name of the module to create or retrieve. 68 | * @param {Array.=} requires If specified then new module is being created. If unspecified then the 69 | * the module is being retrieved for further configuration. 70 | * @param {Function} configFn Option configuration function for the module. Same as 71 | * {@link angular.Module#config Module#config()}. 72 | * @returns {module} new module with the {@link angular.Module} api. 73 | */ 74 | return function module(name, requires, configFn) { 75 | if (requires && modules.hasOwnProperty(name)) { 76 | modules[name] = null; 77 | } 78 | return ensure(modules, name, function() { 79 | if (!requires) { 80 | throw Error('No module: ' + name); 81 | } 82 | 83 | /** @type {!Array.>} */ 84 | var invokeQueue = []; 85 | 86 | /** @type {!Array.} */ 87 | var runBlocks = []; 88 | 89 | var config = invokeLater('$injector', 'invoke'); 90 | 91 | /** @type {angular.Module} */ 92 | var moduleInstance = { 93 | // Private state 94 | _invokeQueue: invokeQueue, 95 | _runBlocks: runBlocks, 96 | 97 | /** 98 | * @ngdoc property 99 | * @name angular.Module#requires 100 | * @propertyOf angular.Module 101 | * @returns {Array.} List of module names which must be loaded before this module. 102 | * @description 103 | * Holds the list of modules which the injector will load before the current module is loaded. 104 | */ 105 | requires: requires, 106 | 107 | /** 108 | * @ngdoc property 109 | * @name angular.Module#name 110 | * @propertyOf angular.Module 111 | * @returns {string} Name of the module. 112 | * @description 113 | */ 114 | name: name, 115 | 116 | 117 | /** 118 | * @ngdoc method 119 | * @name angular.Module#provider 120 | * @methodOf angular.Module 121 | * @param {string} name service name 122 | * @param {Function} providerType Construction function for creating new instance of the service. 123 | * @description 124 | * See {@link AUTO.$provide#provider $provide.provider()}. 125 | */ 126 | provider: invokeLater('$provide', 'provider'), 127 | 128 | /** 129 | * @ngdoc method 130 | * @name angular.Module#factory 131 | * @methodOf angular.Module 132 | * @param {string} name service name 133 | * @param {Function} providerFunction Function for creating new instance of the service. 134 | * @description 135 | * See {@link AUTO.$provide#factory $provide.factory()}. 136 | */ 137 | factory: invokeLater('$provide', 'factory'), 138 | 139 | /** 140 | * @ngdoc method 141 | * @name angular.Module#service 142 | * @methodOf angular.Module 143 | * @param {string} name service name 144 | * @param {Function} constructor A constructor function that will be instantiated. 145 | * @description 146 | * See {@link AUTO.$provide#service $provide.service()}. 147 | */ 148 | service: invokeLater('$provide', 'service'), 149 | 150 | /** 151 | * @ngdoc method 152 | * @name angular.Module#value 153 | * @methodOf angular.Module 154 | * @param {string} name service name 155 | * @param {*} object Service instance object. 156 | * @description 157 | * See {@link AUTO.$provide#value $provide.value()}. 158 | */ 159 | value: invokeLater('$provide', 'value'), 160 | 161 | /** 162 | * @ngdoc method 163 | * @name angular.Module#constant 164 | * @methodOf angular.Module 165 | * @param {string} name constant name 166 | * @param {*} object Constant value. 167 | * @description 168 | * Because the constant are fixed, they get applied before other provide methods. 169 | * See {@link AUTO.$provide#constant $provide.constant()}. 170 | */ 171 | constant: invokeLater('$provide', 'constant', 'unshift'), 172 | 173 | /** 174 | * @ngdoc method 175 | * @name angular.Module#filter 176 | * @methodOf angular.Module 177 | * @param {string} name Filter name. 178 | * @param {Function} filterFactory Factory function for creating new instance of filter. 179 | * @description 180 | * See {@link ng.$filterProvider#register $filterProvider.register()}. 181 | */ 182 | filter: invokeLater('$filterProvider', 'register'), 183 | 184 | /** 185 | * @ngdoc method 186 | * @name angular.Module#controller 187 | * @methodOf angular.Module 188 | * @param {string} name Controller name. 189 | * @param {Function} constructor Controller constructor function. 190 | * @description 191 | * See {@link ng.$controllerProvider#register $controllerProvider.register()}. 192 | */ 193 | controller: invokeLater('$controllerProvider', 'register'), 194 | 195 | /** 196 | * @ngdoc method 197 | * @name angular.Module#directive 198 | * @methodOf angular.Module 199 | * @param {string} name directive name 200 | * @param {Function} directiveFactory Factory function for creating new instance of 201 | * directives. 202 | * @description 203 | * See {@link ng.$compileProvider.directive $compileProvider.directive()}. 204 | */ 205 | directive: invokeLater('$compileProvider', 'directive'), 206 | 207 | /** 208 | * @ngdoc method 209 | * @name angular.Module#config 210 | * @methodOf angular.Module 211 | * @param {Function} configFn Execute this function on module load. Useful for service 212 | * configuration. 213 | * @description 214 | * Use this method to register work which needs to be performed on module loading. 215 | */ 216 | config: config, 217 | 218 | /** 219 | * @ngdoc method 220 | * @name angular.Module#run 221 | * @methodOf angular.Module 222 | * @param {Function} initializationFn Execute this function after injector creation. 223 | * Useful for application initialization. 224 | * @description 225 | * Use this method to register work which needs to be performed when the injector with 226 | * with the current module is finished loading. 227 | */ 228 | run: function(block) { 229 | runBlocks.push(block); 230 | return this; 231 | } 232 | }; 233 | 234 | if (configFn) { 235 | config(configFn); 236 | } 237 | 238 | return moduleInstance; 239 | 240 | /** 241 | * @param {string} provider 242 | * @param {string} method 243 | * @param {String=} insertMethod 244 | * @returns {angular.Module} 245 | */ 246 | function invokeLater(provider, method, insertMethod) { 247 | return function() { 248 | invokeQueue[insertMethod || 'push']([provider, method, arguments]); 249 | return moduleInstance; 250 | } 251 | } 252 | }); 253 | }; 254 | }); 255 | 256 | } 257 | )(window); 258 | 259 | /** 260 | * Closure compiler type information 261 | * 262 | * @typedef { { 263 | * requires: !Array., 264 | * invokeQueue: !Array.>, 265 | * 266 | * service: function(string, Function):angular.Module, 267 | * factory: function(string, Function):angular.Module, 268 | * value: function(string, *):angular.Module, 269 | * 270 | * filter: function(string, Function):angular.Module, 271 | * 272 | * init: function(Function):angular.Module 273 | * } } 274 | */ 275 | angular.Module; 276 | 277 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-loader.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.1 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(i){'use strict';function d(c,b,e){return c[b]||(c[b]=e())}return d(d(i,"angular",Object),"module",function(){var c={};return function(b,e,f){e&&c.hasOwnProperty(b)&&(c[b]=null);return d(c,b,function(){function a(a,b,d){return function(){c[d||"push"]([a,b,arguments]);return g}}if(!e)throw Error("No module: "+b);var c=[],d=[],h=a("$injector","invoke"),g={_invokeQueue:c,_runBlocks:d,requires:e,name:b,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"), 7 | value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:h,run:function(a){d.push(a);return this}};f&&h(f);return g})}})})(window); 8 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-resource.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.1 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) { 7 | 'use strict'; 8 | 9 | /** 10 | * @ngdoc overview 11 | * @name ngResource 12 | * @description 13 | */ 14 | 15 | /** 16 | * @ngdoc object 17 | * @name ngResource.$resource 18 | * @requires $http 19 | * 20 | * @description 21 | * A factory which creates a resource object that lets you interact with 22 | * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. 23 | * 24 | * The returned resource object has action methods which provide high-level behaviors without 25 | * the need to interact with the low level {@link ng.$http $http} service. 26 | * 27 | * @param {string} url A parameterized URL template with parameters prefixed by `:` as in 28 | * `/user/:username`. 29 | * 30 | * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in 31 | * `actions` methods. 32 | * 33 | * Each key value in the parameter object is first bound to url template if present and then any 34 | * excess keys are appended to the url search query after the `?`. 35 | * 36 | * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in 37 | * URL `/path/greet?salutation=Hello`. 38 | * 39 | * If the parameter value is prefixed with `@` then the value of that parameter is extracted from 40 | * the data object (useful for non-GET operations). 41 | * 42 | * @param {Object.=} actions Hash with declaration of custom action that should extend the 43 | * default set of resource actions. The declaration should be created in the following format: 44 | * 45 | * {action1: {method:?, params:?, isArray:?}, 46 | * action2: {method:?, params:?, isArray:?}, 47 | * ...} 48 | * 49 | * Where: 50 | * 51 | * - `action` – {string} – The name of action. This name becomes the name of the method on your 52 | * resource object. 53 | * - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`, 54 | * and `JSONP` 55 | * - `params` – {object=} – Optional set of pre-bound parameters for this action. 56 | * - isArray – {boolean=} – If true then the returned object for this action is an array, see 57 | * `returns` section. 58 | * 59 | * @returns {Object} A resource "class" object with methods for the default set of resource actions 60 | * optionally extended with custom `actions`. The default set contains these actions: 61 | * 62 | * { 'get': {method:'GET'}, 63 | * 'save': {method:'POST'}, 64 | * 'query': {method:'GET', isArray:true}, 65 | * 'remove': {method:'DELETE'}, 66 | * 'delete': {method:'DELETE'} }; 67 | * 68 | * Calling these methods invoke an {@link ng.$http} with the specified http method, 69 | * destination and parameters. When the data is returned from the server then the object is an 70 | * instance of the resource class `save`, `remove` and `delete` actions are available on it as 71 | * methods with the `$` prefix. This allows you to easily perform CRUD operations (create, read, 72 | * update, delete) on server-side data like this: 73 | *
 74 |         var User = $resource('/user/:userId', {userId:'@id'});
 75 |         var user = User.get({userId:123}, function() {
 76 |           user.abc = true;
 77 |           user.$save();
 78 |         });
 79 |      
80 | * 81 | * It is important to realize that invoking a $resource object method immediately returns an 82 | * empty reference (object or array depending on `isArray`). Once the data is returned from the 83 | * server the existing reference is populated with the actual data. This is a useful trick since 84 | * usually the resource is assigned to a model which is then rendered by the view. Having an empty 85 | * object results in no rendering, once the data arrives from the server then the object is 86 | * populated with the data and the view automatically re-renders itself showing the new data. This 87 | * means that in most case one never has to write a callback function for the action methods. 88 | * 89 | * The action methods on the class object or instance object can be invoked with the following 90 | * parameters: 91 | * 92 | * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` 93 | * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` 94 | * - non-GET instance actions: `instance.$action([parameters], [success], [error])` 95 | * 96 | * 97 | * @example 98 | * 99 | * # Credit card resource 100 | * 101 | *
102 |      // Define CreditCard class
103 |      var CreditCard = $resource('/user/:userId/card/:cardId',
104 |       {userId:123, cardId:'@id'}, {
105 |        charge: {method:'POST', params:{charge:true}}
106 |       });
107 | 
108 |      // We can retrieve a collection from the server
109 |      var cards = CreditCard.query(function() {
110 |        // GET: /user/123/card
111 |        // server returns: [ {id:456, number:'1234', name:'Smith'} ];
112 | 
113 |        var card = cards[0];
114 |        // each item is an instance of CreditCard
115 |        expect(card instanceof CreditCard).toEqual(true);
116 |        card.name = "J. Smith";
117 |        // non GET methods are mapped onto the instances
118 |        card.$save();
119 |        // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
120 |        // server returns: {id:456, number:'1234', name: 'J. Smith'};
121 | 
122 |        // our custom method is mapped as well.
123 |        card.$charge({amount:9.99});
124 |        // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
125 |      });
126 | 
127 |      // we can create an instance as well
128 |      var newCard = new CreditCard({number:'0123'});
129 |      newCard.name = "Mike Smith";
130 |      newCard.$save();
131 |      // POST: /user/123/card {number:'0123', name:'Mike Smith'}
132 |      // server returns: {id:789, number:'01234', name: 'Mike Smith'};
133 |      expect(newCard.id).toEqual(789);
134 |  * 
135 | * 136 | * The object returned from this function execution is a resource "class" which has "static" method 137 | * for each action in the definition. 138 | * 139 | * Calling these methods invoke `$http` on the `url` template with the given `method` and `params`. 140 | * When the data is returned from the server then the object is an instance of the resource type and 141 | * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD 142 | * operations (create, read, update, delete) on server-side data. 143 | 144 |
145 |      var User = $resource('/user/:userId', {userId:'@id'});
146 |      var user = User.get({userId:123}, function() {
147 |        user.abc = true;
148 |        user.$save();
149 |      });
150 |    
151 | * 152 | * It's worth noting that the success callback for `get`, `query` and other method gets passed 153 | * in the response that came from the server as well as $http header getter function, so one 154 | * could rewrite the above example and get access to http headers as: 155 | * 156 |
157 |      var User = $resource('/user/:userId', {userId:'@id'});
158 |      User.get({userId:123}, function(u, getResponseHeaders){
159 |        u.abc = true;
160 |        u.$save(function(u, putResponseHeaders) {
161 |          //u => saved user object
162 |          //putResponseHeaders => $http header getter
163 |        });
164 |      });
165 |    
166 | 167 | * # Buzz client 168 | 169 | Let's look at what a buzz client created with the `$resource` service looks like: 170 | 171 | 172 | 192 | 193 |
194 | 195 | 196 |
197 |
198 |

199 | 200 | {{item.actor.name}} 201 | Expand replies: {{item.links.replies[0].count}} 202 |

203 | {{item.object.content | html}} 204 |
205 | 206 | {{reply.actor.name}}: {{reply.content | html}} 207 |
208 |
209 |
210 |
211 | 212 | 213 |
214 | */ 215 | angular.module('ngResource', ['ng']). 216 | factory('$resource', ['$http', '$parse', function($http, $parse) { 217 | var DEFAULT_ACTIONS = { 218 | 'get': {method:'GET'}, 219 | 'save': {method:'POST'}, 220 | 'query': {method:'GET', isArray:true}, 221 | 'remove': {method:'DELETE'}, 222 | 'delete': {method:'DELETE'} 223 | }; 224 | var noop = angular.noop, 225 | forEach = angular.forEach, 226 | extend = angular.extend, 227 | copy = angular.copy, 228 | isFunction = angular.isFunction, 229 | getter = function(obj, path) { 230 | return $parse(path)(obj); 231 | }; 232 | 233 | /** 234 | * We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow 235 | * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path 236 | * segments: 237 | * segment = *pchar 238 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 239 | * pct-encoded = "%" HEXDIG HEXDIG 240 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 241 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 242 | * / "*" / "+" / "," / ";" / "=" 243 | */ 244 | function encodeUriSegment(val) { 245 | return encodeUriQuery(val, true). 246 | replace(/%26/gi, '&'). 247 | replace(/%3D/gi, '='). 248 | replace(/%2B/gi, '+'); 249 | } 250 | 251 | 252 | /** 253 | * This method is intended for encoding *key* or *value* parts of query component. We need a custom 254 | * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be 255 | * encoded per http://tools.ietf.org/html/rfc3986: 256 | * query = *( pchar / "/" / "?" ) 257 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 258 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 259 | * pct-encoded = "%" HEXDIG HEXDIG 260 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 261 | * / "*" / "+" / "," / ";" / "=" 262 | */ 263 | function encodeUriQuery(val, pctEncodeSpaces) { 264 | return encodeURIComponent(val). 265 | replace(/%40/gi, '@'). 266 | replace(/%3A/gi, ':'). 267 | replace(/%24/g, '$'). 268 | replace(/%2C/gi, ','). 269 | replace((pctEncodeSpaces ? null : /%20/g), '+'); 270 | } 271 | 272 | function Route(template, defaults) { 273 | this.template = template = template + '#'; 274 | this.defaults = defaults || {}; 275 | var urlParams = this.urlParams = {}; 276 | forEach(template.split(/\W/), function(param){ 277 | if (param && template.match(new RegExp("[^\\\\]:" + param + "\\W"))) { 278 | urlParams[param] = true; 279 | } 280 | }); 281 | this.template = template.replace(/\\:/g, ':'); 282 | } 283 | 284 | Route.prototype = { 285 | url: function(params) { 286 | var self = this, 287 | url = this.template, 288 | encodedVal; 289 | 290 | params = params || {}; 291 | forEach(this.urlParams, function(_, urlParam){ 292 | encodedVal = encodeUriSegment(params[urlParam] || self.defaults[urlParam] || ""); 293 | url = url.replace(new RegExp(":" + urlParam + "(\\W)"), encodedVal + "$1"); 294 | }); 295 | url = url.replace(/\/?#$/, ''); 296 | var query = []; 297 | forEach(params, function(value, key){ 298 | if (!self.urlParams[key]) { 299 | query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value)); 300 | } 301 | }); 302 | query.sort(); 303 | url = url.replace(/\/*$/, ''); 304 | return url + (query.length ? '?' + query.join('&') : ''); 305 | } 306 | }; 307 | 308 | 309 | function ResourceFactory(url, paramDefaults, actions) { 310 | var route = new Route(url); 311 | 312 | actions = extend({}, DEFAULT_ACTIONS, actions); 313 | 314 | function extractParams(data){ 315 | var ids = {}; 316 | forEach(paramDefaults || {}, function(value, key){ 317 | ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value; 318 | }); 319 | return ids; 320 | } 321 | 322 | function Resource(value){ 323 | copy(value || {}, this); 324 | } 325 | 326 | forEach(actions, function(action, name) { 327 | var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH'; 328 | Resource[name] = function(a1, a2, a3, a4) { 329 | var params = {}; 330 | var data; 331 | var success = noop; 332 | var error = null; 333 | switch(arguments.length) { 334 | case 4: 335 | error = a4; 336 | success = a3; 337 | //fallthrough 338 | case 3: 339 | case 2: 340 | if (isFunction(a2)) { 341 | if (isFunction(a1)) { 342 | success = a1; 343 | error = a2; 344 | break; 345 | } 346 | 347 | success = a2; 348 | error = a3; 349 | //fallthrough 350 | } else { 351 | params = a1; 352 | data = a2; 353 | success = a3; 354 | break; 355 | } 356 | case 1: 357 | if (isFunction(a1)) success = a1; 358 | else if (hasBody) data = a1; 359 | else params = a1; 360 | break; 361 | case 0: break; 362 | default: 363 | throw "Expected between 0-4 arguments [params, data, success, error], got " + 364 | arguments.length + " arguments."; 365 | } 366 | 367 | var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data)); 368 | $http({ 369 | method: action.method, 370 | url: route.url(extend({}, extractParams(data), action.params || {}, params)), 371 | data: data 372 | }).then(function(response) { 373 | var data = response.data; 374 | 375 | if (data) { 376 | if (action.isArray) { 377 | value.length = 0; 378 | forEach(data, function(item) { 379 | value.push(new Resource(item)); 380 | }); 381 | } else { 382 | copy(data, value); 383 | } 384 | } 385 | (success||noop)(value, response.headers); 386 | }, error); 387 | 388 | return value; 389 | }; 390 | 391 | 392 | Resource.bind = function(additionalParamDefaults){ 393 | return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); 394 | }; 395 | 396 | 397 | Resource.prototype['$' + name] = function(a1, a2, a3) { 398 | var params = extractParams(this), 399 | success = noop, 400 | error; 401 | 402 | switch(arguments.length) { 403 | case 3: params = a1; success = a2; error = a3; break; 404 | case 2: 405 | case 1: 406 | if (isFunction(a1)) { 407 | success = a1; 408 | error = a2; 409 | } else { 410 | params = a1; 411 | success = a2 || noop; 412 | } 413 | case 0: break; 414 | default: 415 | throw "Expected between 1-3 arguments [params, success, error], got " + 416 | arguments.length + " arguments."; 417 | } 418 | var data = hasBody ? this : undefined; 419 | Resource[name].call(this, params, data, success, error); 420 | }; 421 | }); 422 | return Resource; 423 | } 424 | 425 | return ResourceFactory; 426 | }]); 427 | 428 | })(window, window.angular); 429 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-resource.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.1 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(A,f,u){'use strict';f.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(v,w){function g(b,c){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(c?null:/%20/g,"+")}function l(b,c){this.template=b+="#";this.defaults=c||{};var a=this.urlParams={};j(b.split(/\W/),function(c){c&&b.match(RegExp("[^\\\\]:"+c+"\\W"))&&(a[c]=!0)});this.template=b.replace(/\\:/g,":")}function s(b,c,a){function f(d){var b= 7 | {};j(c||{},function(a,x){var m;a.charAt&&a.charAt(0)=="@"?(m=a.substr(1),m=w(m)(d)):m=a;b[x]=m});return b}function e(a){t(a||{},this)}var y=new l(b),a=r({},z,a);j(a,function(d,g){var l=d.method=="POST"||d.method=="PUT"||d.method=="PATCH";e[g]=function(a,b,c,g){var i={},h,k=o,p=null;switch(arguments.length){case 4:p=g,k=c;case 3:case 2:if(q(b)){if(q(a)){k=a;p=b;break}k=b;p=c}else{i=a;h=b;k=c;break}case 1:q(a)?k=a:l?h=a:i=a;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+ 8 | arguments.length+" arguments.";}var n=this instanceof e?this:d.isArray?[]:new e(h);v({method:d.method,url:y.url(r({},f(h),d.params||{},i)),data:h}).then(function(a){var b=a.data;if(b)d.isArray?(n.length=0,j(b,function(a){n.push(new e(a))})):t(b,n);(k||o)(n,a.headers)},p);return n};e.bind=function(d){return s(b,r({},c,d),a)};e.prototype["$"+g]=function(a,b,d){var c=f(this),i=o,h;switch(arguments.length){case 3:c=a;i=b;h=d;break;case 2:case 1:q(a)?(i=a,h=b):(c=a,i=b||o);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+ 9 | arguments.length+" arguments.";}e[g].call(this,c,l?this:u,i,h)}});return e}var z={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},o=f.noop,j=f.forEach,r=f.extend,t=f.copy,q=f.isFunction;l.prototype={url:function(b){var c=this,a=this.template,f,b=b||{};j(this.urlParams,function(e,d){f=g(b[d]||c.defaults[d]||"",!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+");a=a.replace(RegExp(":"+d+"(\\W)"),f+"$1")});var a= 10 | a.replace(/\/?#$/,""),e=[];j(b,function(a,b){c.urlParams[b]||e.push(g(b)+"="+g(a))});e.sort();a=a.replace(/\/*$/,"");return a+(e.length?"?"+e.join("&"):"")}};return s}])})(window,window.angular); 11 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-sanitize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.1 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular, undefined) { 7 | 'use strict'; 8 | 9 | /** 10 | * @ngdoc overview 11 | * @name ngSanitize 12 | * @description 13 | */ 14 | 15 | /* 16 | * HTML Parser By Misko Hevery (misko@hevery.com) 17 | * based on: HTML Parser By John Resig (ejohn.org) 18 | * Original code by Erik Arvidsson, Mozilla Public License 19 | * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js 20 | * 21 | * // Use like so: 22 | * htmlParser(htmlString, { 23 | * start: function(tag, attrs, unary) {}, 24 | * end: function(tag) {}, 25 | * chars: function(text) {}, 26 | * comment: function(text) {} 27 | * }); 28 | * 29 | */ 30 | 31 | 32 | /** 33 | * @ngdoc service 34 | * @name ngSanitize.$sanitize 35 | * @function 36 | * 37 | * @description 38 | * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are 39 | * then serialized back to properly escaped html string. This means that no unsafe input can make 40 | * it into the returned string, however, since our parser is more strict than a typical browser 41 | * parser, it's possible that some obscure input, which would be recognized as valid HTML by a 42 | * browser, won't make it through the sanitizer. 43 | * 44 | * @param {string} html Html input. 45 | * @returns {string} Sanitized html. 46 | * 47 | * @example 48 | 49 | 50 | 58 |
59 | Snippet: 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
FilterSourceRendered
html filter 69 |
<div ng-bind-html="snippet">
</div>
70 |
72 |
73 |
no filter
<div ng-bind="snippet">
</div>
unsafe html filter
<div ng-bind-html-unsafe="snippet">
</div>
86 |
87 |
88 | 89 | it('should sanitize the html snippet ', function() { 90 | expect(using('#html-filter').element('div').html()). 91 | toBe('

an html\nclick here\nsnippet

'); 92 | }); 93 | 94 | it('should escape snippet without any filter', function() { 95 | expect(using('#escaped-html').element('div').html()). 96 | toBe("<p style=\"color:blue\">an html\n" + 97 | "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 98 | "snippet</p>"); 99 | }); 100 | 101 | it('should inline raw snippet if filtered as unsafe', function() { 102 | expect(using('#html-unsafe-filter').element("div").html()). 103 | toBe("

an html\n" + 104 | "click here\n" + 105 | "snippet

"); 106 | }); 107 | 108 | it('should update', function() { 109 | input('snippet').enter('new text'); 110 | expect(using('#html-filter').binding('snippet')).toBe('new text'); 111 | expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>"); 112 | expect(using('#html-unsafe-filter').binding("snippet")).toBe('new text'); 113 | }); 114 |
115 |
116 | */ 117 | var $sanitize = function(html) { 118 | var buf = []; 119 | htmlParser(html, htmlSanitizeWriter(buf)); 120 | return buf.join(''); 121 | }; 122 | 123 | 124 | // Regular Expressions for parsing tags and attributes 125 | var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, 126 | END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, 127 | ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, 128 | BEGIN_TAG_REGEXP = /^/g, 131 | CDATA_REGEXP = //g, 132 | URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/, 133 | NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character) 134 | 135 | 136 | // Good source of info about elements and attributes 137 | // http://dev.w3.org/html5/spec/Overview.html#semantics 138 | // http://simon.html5.org/html-elements 139 | 140 | // Safe Void Elements - HTML5 141 | // http://dev.w3.org/html5/spec/Overview.html#void-elements 142 | var voidElements = makeMap("area,br,col,hr,img,wbr"); 143 | 144 | // Elements that you can, intentionally, leave open (and which close themselves) 145 | // http://dev.w3.org/html5/spec/Overview.html#optional-tags 146 | var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), 147 | optionalEndTagInlineElements = makeMap("rp,rt"), 148 | optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements); 149 | 150 | // Safe Block Elements - HTML5 151 | var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," + 152 | "blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," + 153 | "header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")); 154 | 155 | // Inline Elements - HTML5 156 | var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," + 157 | "big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," + 158 | "span,strike,strong,sub,sup,time,tt,u,var")); 159 | 160 | 161 | // Special Elements (can contain anything) 162 | var specialElements = makeMap("script,style"); 163 | 164 | var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements); 165 | 166 | //Attributes that have href and hence need to be sanitized 167 | var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap"); 168 | var validAttrs = angular.extend({}, uriAttrs, makeMap( 169 | 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ 170 | 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ 171 | 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ 172 | 'scope,scrolling,shape,span,start,summary,target,title,type,'+ 173 | 'valign,value,vspace,width')); 174 | 175 | function makeMap(str) { 176 | var obj = {}, items = str.split(','), i; 177 | for (i = 0; i < items.length; i++) obj[items[i]] = true; 178 | return obj; 179 | } 180 | 181 | 182 | /** 183 | * @example 184 | * htmlParser(htmlString, { 185 | * start: function(tag, attrs, unary) {}, 186 | * end: function(tag) {}, 187 | * chars: function(text) {}, 188 | * comment: function(text) {} 189 | * }); 190 | * 191 | * @param {string} html string 192 | * @param {object} handler 193 | */ 194 | function htmlParser( html, handler ) { 195 | var index, chars, match, stack = [], last = html; 196 | stack.last = function() { return stack[ stack.length - 1 ]; }; 197 | 198 | while ( html ) { 199 | chars = true; 200 | 201 | // Make sure we're not in a script or style element 202 | if ( !stack.last() || !specialElements[ stack.last() ] ) { 203 | 204 | // Comment 205 | if ( html.indexOf(""); 207 | 208 | if ( index >= 0 ) { 209 | if (handler.comment) handler.comment( html.substring( 4, index ) ); 210 | html = html.substring( index + 3 ); 211 | chars = false; 212 | } 213 | 214 | // end tag 215 | } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { 216 | match = html.match( END_TAG_REGEXP ); 217 | 218 | if ( match ) { 219 | html = html.substring( match[0].length ); 220 | match[0].replace( END_TAG_REGEXP, parseEndTag ); 221 | chars = false; 222 | } 223 | 224 | // start tag 225 | } else if ( BEGIN_TAG_REGEXP.test(html) ) { 226 | match = html.match( START_TAG_REGEXP ); 227 | 228 | if ( match ) { 229 | html = html.substring( match[0].length ); 230 | match[0].replace( START_TAG_REGEXP, parseStartTag ); 231 | chars = false; 232 | } 233 | } 234 | 235 | if ( chars ) { 236 | index = html.indexOf("<"); 237 | 238 | var text = index < 0 ? html : html.substring( 0, index ); 239 | html = index < 0 ? "" : html.substring( index ); 240 | 241 | if (handler.chars) handler.chars( decodeEntities(text) ); 242 | } 243 | 244 | } else { 245 | html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){ 246 | text = text. 247 | replace(COMMENT_REGEXP, "$1"). 248 | replace(CDATA_REGEXP, "$1"); 249 | 250 | if (handler.chars) handler.chars( decodeEntities(text) ); 251 | 252 | return ""; 253 | }); 254 | 255 | parseEndTag( "", stack.last() ); 256 | } 257 | 258 | if ( html == last ) { 259 | throw "Parse Error: " + html; 260 | } 261 | last = html; 262 | } 263 | 264 | // Clean up any remaining tags 265 | parseEndTag(); 266 | 267 | function parseStartTag( tag, tagName, rest, unary ) { 268 | tagName = angular.lowercase(tagName); 269 | if ( blockElements[ tagName ] ) { 270 | while ( stack.last() && inlineElements[ stack.last() ] ) { 271 | parseEndTag( "", stack.last() ); 272 | } 273 | } 274 | 275 | if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) { 276 | parseEndTag( "", tagName ); 277 | } 278 | 279 | unary = voidElements[ tagName ] || !!unary; 280 | 281 | if ( !unary ) 282 | stack.push( tagName ); 283 | 284 | var attrs = {}; 285 | 286 | rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) { 287 | var value = doubleQuotedValue 288 | || singleQoutedValue 289 | || unqoutedValue 290 | || ''; 291 | 292 | attrs[name] = decodeEntities(value); 293 | }); 294 | if (handler.start) handler.start( tagName, attrs, unary ); 295 | } 296 | 297 | function parseEndTag( tag, tagName ) { 298 | var pos = 0, i; 299 | tagName = angular.lowercase(tagName); 300 | if ( tagName ) 301 | // Find the closest opened tag of the same type 302 | for ( pos = stack.length - 1; pos >= 0; pos-- ) 303 | if ( stack[ pos ] == tagName ) 304 | break; 305 | 306 | if ( pos >= 0 ) { 307 | // Close all the open elements, up the stack 308 | for ( i = stack.length - 1; i >= pos; i-- ) 309 | if (handler.end) handler.end( stack[ i ] ); 310 | 311 | // Remove the open elements from the stack 312 | stack.length = pos; 313 | } 314 | } 315 | } 316 | 317 | /** 318 | * decodes all entities into regular string 319 | * @param value 320 | * @returns {string} A string with decoded entities. 321 | */ 322 | var hiddenPre=document.createElement("pre"); 323 | function decodeEntities(value) { 324 | hiddenPre.innerHTML=value.replace(//g, '>'); 343 | } 344 | 345 | /** 346 | * create an HTML/XML writer which writes to buffer 347 | * @param {Array} buf use buf.jain('') to get out sanitized html string 348 | * @returns {object} in the form of { 349 | * start: function(tag, attrs, unary) {}, 350 | * end: function(tag) {}, 351 | * chars: function(text) {}, 352 | * comment: function(text) {} 353 | * } 354 | */ 355 | function htmlSanitizeWriter(buf){ 356 | var ignore = false; 357 | var out = angular.bind(buf, buf.push); 358 | return { 359 | start: function(tag, attrs, unary){ 360 | tag = angular.lowercase(tag); 361 | if (!ignore && specialElements[tag]) { 362 | ignore = tag; 363 | } 364 | if (!ignore && validElements[tag] == true) { 365 | out('<'); 366 | out(tag); 367 | angular.forEach(attrs, function(value, key){ 368 | var lkey=angular.lowercase(key); 369 | if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { 370 | out(' '); 371 | out(key); 372 | out('="'); 373 | out(encodeEntities(value)); 374 | out('"'); 375 | } 376 | }); 377 | out(unary ? '/>' : '>'); 378 | } 379 | }, 380 | end: function(tag){ 381 | tag = angular.lowercase(tag); 382 | if (!ignore && validElements[tag] == true) { 383 | out(''); 386 | } 387 | if (tag == ignore) { 388 | ignore = false; 389 | } 390 | }, 391 | chars: function(chars){ 392 | if (!ignore) { 393 | out(encodeEntities(chars)); 394 | } 395 | } 396 | }; 397 | } 398 | 399 | 400 | // define ngSanitize module and register $sanitize service 401 | angular.module('ngSanitize', []).value('$sanitize', $sanitize); 402 | 403 | /** 404 | * @ngdoc directive 405 | * @name ngSanitize.directive:ngBindHtml 406 | * 407 | * @description 408 | * Creates a binding that will sanitize the result of evaluating the `expression` with the 409 | * {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element. 410 | * 411 | * See {@link ngSanitize.$sanitize $sanitize} docs for examples. 412 | * 413 | * @element ANY 414 | * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. 415 | */ 416 | angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) { 417 | return function(scope, element, attr) { 418 | element.addClass('ng-binding').data('$binding', attr.ngBindHtml); 419 | scope.$watch(attr.ngBindHtml, function(value) { 420 | value = $sanitize(value); 421 | element.html(value || ''); 422 | }); 423 | }; 424 | }]); 425 | /** 426 | * @ngdoc filter 427 | * @name ngSanitize.filter:linky 428 | * @function 429 | * 430 | * @description 431 | * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and 432 | * plain email address links. 433 | * 434 | * @param {string} text Input text. 435 | * @returns {string} Html-linkified text. 436 | * 437 | * @example 438 | 439 | 440 | 450 |
451 | Snippet: 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 463 | 466 | 467 | 468 | 469 | 470 | 471 | 472 |
FilterSourceRendered
linky filter 461 |
<div ng-bind-html="snippet | linky">
</div>
462 |
464 |
465 |
no filter
<div ng-bind="snippet">
</div>
473 | 474 | 475 | it('should linkify the snippet with urls', function() { 476 | expect(using('#linky-filter').binding('snippet | linky')). 477 | toBe('Pretty text with some links: ' + 478 | 'http://angularjs.org/, ' + 479 | 'us@somewhere.org, ' + 480 | 'another@somewhere.org, ' + 481 | 'and one more: ftp://127.0.0.1/.'); 482 | }); 483 | 484 | it ('should not linkify snippet without the linky filter', function() { 485 | expect(using('#escaped-html').binding('snippet')). 486 | toBe("Pretty text with some links:\n" + 487 | "http://angularjs.org/,\n" + 488 | "mailto:us@somewhere.org,\n" + 489 | "another@somewhere.org,\n" + 490 | "and one more: ftp://127.0.0.1/."); 491 | }); 492 | 493 | it('should update', function() { 494 | input('snippet').enter('new http://link.'); 495 | expect(using('#linky-filter').binding('snippet | linky')). 496 | toBe('new http://link.'); 497 | expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); 498 | }); 499 | 500 | 501 | */ 502 | angular.module('ngSanitize').filter('linky', function() { 503 | var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/, 504 | MAILTO_REGEXP = /^mailto:/; 505 | 506 | return function(text) { 507 | if (!text) return text; 508 | var match; 509 | var raw = text; 510 | var html = []; 511 | // TODO(vojta): use $sanitize instead 512 | var writer = htmlSanitizeWriter(html); 513 | var url; 514 | var i; 515 | while ((match = raw.match(LINKY_URL_REGEXP))) { 516 | // We can not end in these as they are sometimes found at the end of the sentence 517 | url = match[0]; 518 | // if we did not match ftp/http/mailto then assume mailto 519 | if (match[2] == match[3]) url = 'mailto:' + url; 520 | i = match.index; 521 | writer.chars(raw.substr(0, i)); 522 | writer.start('a', {href:url}); 523 | writer.chars(match[0].replace(MAILTO_REGEXP, '')); 524 | writer.end('a'); 525 | raw = raw.substring(i + match[0].length); 526 | } 527 | writer.chars(raw); 528 | return html.join(''); 529 | }; 530 | }); 531 | 532 | })(window, window.angular); 533 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/angular-sanitize.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.1 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(I,g){'use strict';function i(a){var d={},a=a.split(","),b;for(b=0;b=0;e--)if(f[e]==b)break;if(e>=0){for(c=f.length-1;c>=e;c--)d.end&&d.end(f[c]);f.length= 7 | e}}var c,h,f=[],j=a;for(f.last=function(){return f[f.length-1]};a;){h=!0;if(!f.last()||!q[f.last()]){if(a.indexOf("<\!--")===0)c=a.indexOf("--\>"),c>=0&&(d.comment&&d.comment(a.substring(4,c)),a=a.substring(c+3),h=!1);else if(B.test(a)){if(c=a.match(r))a=a.substring(c[0].length),c[0].replace(r,e),h=!1}else if(C.test(a)&&(c=a.match(s)))a=a.substring(c[0].length),c[0].replace(s,b),h=!1;h&&(c=a.indexOf("<"),h=c<0?a:a.substring(0,c),a=c<0?"":a.substring(c),d.chars&&d.chars(k(h)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+ 8 | f.last()+"[^>]*>","i"),function(b,a){a=a.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(a));return""}),e("",f.last());if(a==j)throw"Parse Error: "+a;j=a}e()}function k(a){l.innerHTML=a.replace(//g,">")}function u(a){var d=!1,b=g.bind(a,a.push);return{start:function(a,c,h){a=g.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]== 9 | !0&&(b("<"),b(a),g.forEach(c,function(a,c){var e=g.lowercase(c);if(G[e]==!0&&(w[e]!==!0||a.match(H)))b(" "),b(c),b('="'),b(t(a)),b('"')}),b(h?"/>":">"))},end:function(a){a=g.lowercase(a);!d&&v[a]==!0&&(b(""));a==d&&(d=!1)},chars:function(a){d||b(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^/g, 10 | E=//g,H=/^((ftp|https?):\/\/|mailto:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=g.extend({},y,x),m=g.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=g.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), 11 | q=i("script,style"),v=g.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=g.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");g.module("ngSanitize",[]).value("$sanitize",function(a){var d=[]; 12 | z(a,u(d));return d.join("")});g.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,b,e){b.addClass("ng-binding").data("$binding",e.ngBindHtml);d.$watch(e.ngBindHtml,function(c){c=a(c);b.html(c||"")})}}]);g.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(b){if(!b)return b;for(var e=b,c=[],h=u(c),f,g;b=e.match(a);)f=b[0],b[2]==b[3]&&(f="mailto:"+f),g=b.index, 13 | h.chars(e.substr(0,g)),h.start("a",{href:f}),h.chars(b[0].replace(d,"")),h.end("a"),e=e.substring(g+b[0].length);h.chars(e);return c.join("")}})})(window,window.angular); 14 | -------------------------------------------------------------------------------- /src/main/webapp/lib/angular/version.txt: -------------------------------------------------------------------------------- 1 | 1.0.1 2 | -------------------------------------------------------------------------------- /src/main/webapp/lib/jquery/jquery-1.8.0.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pjiricka/affablebean-angularjs-ee7/ac25bc2705a2b6b851d47b42c7ad00ac1758a09f/src/main/webapp/lib/jquery/jquery-1.8.0.js -------------------------------------------------------------------------------- /src/main/webapp/partials/cart.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 6 |
7 |

Your subtotal is {{total}}

8 |
9 |
10 | 11 |
12 |
13 | 14 | 15 |
16 |
17 |

The content of your cart is:

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
{{product.name}}{{product.description}}{{product.price}}
27 |
28 |
29 | -------------------------------------------------------------------------------- /src/main/webapp/partials/checkout.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

Checkout

6 |

In order to purchase the items in your shopping cart, please provide us with the following information:

7 |
8 |
9 | 10 |
11 | 12 |
13 | Name is required 14 |
15 |
16 |
17 |
18 | 19 |
20 | 21 |
22 | Valid email is required 23 |
24 |
25 |
26 |
27 | 28 |
29 | 30 |
31 | Name is required 32 |
33 |
34 |
35 |
36 | 37 |
38 | 39 |
40 | Name is required 41 |
42 |
43 |
44 |
45 | 46 |
47 | 48 |
49 | Name is required 50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 | 59 |
60 |

Your order has been processed.

61 |

The order confirmation number is {{orderId}}

62 | 63 |
64 | 65 | 66 |
67 |
    68 |
  • Next-day delivery is guaranteed
  • 69 |
  • A 3.00 delivery surcharge is applied to all purchase orders
  • 70 |
71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
Subtotal:{{subtotal}}
Delivery surcharge:{{surcharge}}
Total:{{total}}
85 |
86 | 87 |
88 | -------------------------------------------------------------------------------- /src/main/webapp/partials/department.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 11 |
12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
{{product.name}}{{product.description}}{{product.price}}
24 |
25 | 26 |
27 | -------------------------------------------------------------------------------- /src/main/webapp/partials/main.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

The Affable Bean

6 |

Welcome to the online home of the Affable Bean Green Grocer. Our unique home delivery service brings you fresh organic produce, dairy, meats, breads and other delicious and healthy items direct to your doorstep.

7 |
8 | 9 | 10 |
11 |
12 |

Dairy

13 |

14 |
15 |
16 |

Meats

17 |

18 |
19 |
20 |

Bakery

21 |

22 |
23 |
24 |

Fruit & Veges

25 |

26 |
27 |
28 | 29 |
30 | --------------------------------------------------------------------------------