From 82bf485d1e502f940ba856cf55f058b193f1fd6f Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sat, 10 Mar 2018 17:21:31 +0100
Subject: [PATCH] #291 - improve input validation; ftl variables are now
 accessible without model

---
 .../controller/CategoryController.java        | 35 +++++++------------
 .../validators/CategoryValidator.java         |  4 +--
 src/main/resources/languages/_de.properties   |  2 +-
 src/main/resources/languages/_en.properties   |  2 +-
 .../templates/categories/categories.ftl       |  8 ++---
 .../templates/categories/newCategory.ftl      | 23 ++++++------
 src/main/resources/templates/validation.ftl   |  8 +++++
 7 files changed, 38 insertions(+), 44 deletions(-)
 create mode 100644 src/main/resources/templates/validation.ftl

diff --git a/src/main/java/de/deadlocker8/budgetmaster/controller/CategoryController.java b/src/main/java/de/deadlocker8/budgetmaster/controller/CategoryController.java
index aa85bf70e..ef86b19b1 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/controller/CategoryController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/controller/CategoryController.java
@@ -1,27 +1,21 @@
 package de.deadlocker8.budgetmaster.controller;
 
-import de.deadlocker8.budgetmaster.Validators.CategoryValidator;
 import de.deadlocker8.budgetmaster.entities.Category;
 import de.deadlocker8.budgetmaster.entities.CategoryType;
 import de.deadlocker8.budgetmaster.repositories.CategoryRepository;
-import de.deadlocker8.budgetmaster.utils.*;
-import freemarker.ext.beans.BeansWrapper;
-import freemarker.template.TemplateHashModel;
+import de.deadlocker8.budgetmaster.utils.Colors;
+import de.deadlocker8.budgetmaster.utils.Helpers;
+import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException;
+import de.deadlocker8.budgetmaster.validators.CategoryValidator;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Controller;
-import org.springframework.ui.ModelMap;
+import org.springframework.ui.Model;
 import org.springframework.validation.BindingResult;
-import org.springframework.validation.ObjectError;
-import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import tools.ConvertTo;
-import tools.Localization;
-
-import java.util.ArrayList;
 
 
 @Controller
@@ -32,14 +26,14 @@ public class CategoryController extends BaseController
 
 
 	@RequestMapping("/categories")
-	public String index(@ModelAttribute("model") ModelMap model)
+	public String index(Model model)
 	{
 		model.addAttribute("categories", categoryRepository.findAllByOrderByNameAsc());
 		return "categories/categories";
 	}
 
 	@RequestMapping("/categories/{ID}/requestDelete")
-	public String requestDeleteCategory(@ModelAttribute("model") ModelMap model, @PathVariable("ID") Integer ID)
+	public String requestDeleteCategory(Model model, @PathVariable("ID") Integer ID)
 	{
 		if(!isDeletable(ID))
 		{
@@ -53,7 +47,7 @@ public class CategoryController extends BaseController
 	}
 
 	@RequestMapping("/categories/{ID}/delete")
-	public String deleteCategory(@ModelAttribute("model") ModelMap model, @PathVariable("ID") Integer ID)
+	public String deleteCategory(Model model, @PathVariable("ID") Integer ID)
 	{
 		if(isDeletable(ID))
 		{
@@ -70,7 +64,7 @@ public class CategoryController extends BaseController
 	}
 
 	@RequestMapping("/categories/newCategory")
-	public String newCategory(@ModelAttribute("model") ModelMap model)
+	public String newCategory(Model model)
 	{
 		//TODO: add color picker for custom colors
 
@@ -82,7 +76,7 @@ public class CategoryController extends BaseController
 	}
 
 	@RequestMapping("/categories/{ID}/edit")
-	public String editCategory(@ModelAttribute("model") ModelMap model, @PathVariable("ID") Integer ID)
+	public String editCategory(Model model, @PathVariable("ID") Integer ID)
 	{
 		Category category = categoryRepository.findOne(ID);
 		if(category == null)
@@ -104,20 +98,15 @@ public class CategoryController extends BaseController
 	}
 
 	@RequestMapping(value = "/categories/newCategory", method = RequestMethod.POST)
-	public String post(@ModelAttribute("model") ModelMap model, @ModelAttribute("NewCategory") Category category, BindingResult bindingResult)
+	public String post(Model model, @ModelAttribute("NewCategory") Category category, BindingResult bindingResult)
 	{
 		CategoryValidator userValidator = new CategoryValidator();
 		userValidator.validate(category, bindingResult);
 
 		if(bindingResult.hasErrors())
 		{
-			ArrayList<Notification> notifications = new ArrayList<>();
-			for(ObjectError error : bindingResult.getAllErrors())
-			{
-				notifications.add(new Notification(NotificationLevel.WARNING, null, Localization.getString(error.getCode())));
-			}
+			model.addAttribute("error", bindingResult);
 
-			model.addAttribute("notifications", notifications);
 			if(Helpers.getCategoryColorList().contains(category.getColor()))
 			{
 				model.addAttribute("customColor", "#FFFFFF");
diff --git a/src/main/java/de/deadlocker8/budgetmaster/validators/CategoryValidator.java b/src/main/java/de/deadlocker8/budgetmaster/validators/CategoryValidator.java
index 3a9c3db98..a7272ae90 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/validators/CategoryValidator.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/validators/CategoryValidator.java
@@ -1,4 +1,4 @@
-package de.deadlocker8.budgetmaster.Validators;
+package de.deadlocker8.budgetmaster.validators;
 
 import de.deadlocker8.budgetmaster.entities.Category;
 import de.deadlocker8.budgetmaster.utils.Strings;
@@ -19,4 +19,4 @@ public class CategoryValidator implements Validator
 		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", Strings.WARNING_EMPTY_CATEGORY_NAME);
 		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "color", Strings.WARNING_EMPTY_CATEGORY_COLOR);
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties
index 2e20b7dab..0751b4ea5 100644
--- a/src/main/resources/languages/_de.properties
+++ b/src/main/resources/languages/_de.properties
@@ -179,7 +179,7 @@ warning.integer.width.in.pixels=Nur ganzahlige Werte sind f
 warning.empty.height.in.pixels=Bitte gib eine H�he in Pixeln an.
 warning.integer.height.in.pixels=Nur ganzahlige Werte sind f�r das Feld H�he erlaubt.
 warning.empty.savepath.chart=W�hle einen Speicherort f�r das Diagramm aus.
-warning.empty.category.name=Das Feld f�r den Namen darf nicht leer sein.
+warning.empty.category.name=Bitte gib einen Namen ein.
 warning.empty.category.color=Die Kategoriefarbe darf nicht leer sein.
 warning.empty.payment.name=Das Feld f�r den Namen darf nicht leer sein.
 warning.name.character.limit.reached.45=Der Name darf maximal 45 Zeichen lang sein.
diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties
index 46285c6a6..c9ad1a60d 100644
--- a/src/main/resources/languages/_en.properties
+++ b/src/main/resources/languages/_en.properties
@@ -179,7 +179,7 @@ warning.integer.width.in.pixels=Only integer values are allowed for the width fi
 warning.empty.height.in.pixels=Please enter a height in pixels.
 warning.integer.height.in.pixels=Only integer values are allowed for the height field.
 warning.empty.savepath.chart=Please select a location where you want to save the chart.
-warning.empty.category.name=The field for the name can not be empty.
+warning.empty.category.name=Please insert a name.
 warning.empty.category.color=The category color should not be empty.
 warning.empty.payment.name=The field for the name can not be empty.
 warning.name.character.limit.reached.45=The name must not exceed 45 characters in length.
diff --git a/src/main/resources/templates/categories/categories.ftl b/src/main/resources/templates/categories/categories.ftl
index e403d22ef..2cc321165 100644
--- a/src/main/resources/templates/categories/categories.ftl
+++ b/src/main/resources/templates/categories/categories.ftl
@@ -21,7 +21,7 @@
                 <br>
                 <div class="container">
                     <table class="bordered">
-                        <#list model["categories"] as category>
+                        <#list categories as category>
                         <tr>
                             <td>
                                 <div class="category-circle" style="background-color: ${category.color}">
@@ -43,16 +43,16 @@
                 </div>
             </div>
 
-            <#if model["currentCategory"]??>
+            <#if currentCategory??>
                 <!-- confirm delete modal -->
                 <div id="modalConfirmDelete" class="modal">
                     <div class="modal-content">
                         <h4>Kategorie löschen</h4>
-                        <p>Möchtest du die Kategorie "${model["currentCategory"].name}" wirklich löschen?</p>
+                        <p>Möchtest du die Kategorie "${currentCategory.name}" wirklich löschen?</p>
                     </div>
                     <div class="modal-footer">
                         <a href="#" class="modal-action modal-close waves-effect waves-red btn-flat ">Abbrechen</a>
-                        <a href="/categories/${model["currentCategory"].ID}/delete" class="modal-action modal-close waves-effect waves-green btn-flat ">Löschen</a>
+                        <a href="/categories/${currentCategory.ID}/delete" class="modal-action modal-close waves-effect waves-green btn-flat ">Löschen</a>
                     </div>
                 </div>
             </#if>
diff --git a/src/main/resources/templates/categories/newCategory.ftl b/src/main/resources/templates/categories/newCategory.ftl
index 80300ae91..77671928b 100644
--- a/src/main/resources/templates/categories/newCategory.ftl
+++ b/src/main/resources/templates/categories/newCategory.ftl
@@ -13,38 +13,39 @@
             <div class="card main-card">
                 <div class="container">
                     <div class="section center-align">
-                        <div class="grey-text text-darken-4 headline"><#if model["category"].getID()??>${locale.getString("title.category.edit")}<#else>${locale.getString("title.category.new")}</#if></div>
+                        <div class="grey-text text-darken-4 headline"><#if category.getID()??>${locale.getString("title.category.edit")}<#else>${locale.getString("title.category.new")}</#if></div>
                     </div>
                 </div>
                 <div class="container">
+                     <#import "../validation.ftl" as validation>
                     <form name="NewCategory" action="/categories/newCategory" method="post">
-                        <input type="hidden" name="ID" value="<#if model["category"].getID()??>${model["category"].getID()}</#if>">
+                        <input type="hidden" name="ID" value="<#if category.getID()??>${category.getID()}</#if>">
                         <div class="row">
                             <div class="input-field col s12 m12 l8 offset-l2">
-                                <input id="category-name" type="text" name="name" value="<#if model["category"].getName()??>${model["category"].getName()}</#if>">
+                                <input id="category-name" type="text" name="name" <@validation.validation "name"/> value="<#if category.getName()??>${category.getName()}</#if>">
                                 <label for="category-name">${locale.getString("category.new.label.name")}</label>
                             </div>
                         </div>
-                        <input type="hidden" name="color" id="categoryColor" value="${model["category"].getColor()}">
+                        <input type="hidden" name="color" id="categoryColor" value="${category.getColor()}">
                         <#list categoryColors as color>
                             <#if color?counter == 1 || color?counter == 7 || color?counter == 13>
                                 <div class="row">
                                     <div class="col s2 m1 offset-m3 no-padding">
-                                        <div class="category-color <#if color == model["category"].getColor()>category-color-active</#if>" style="background-color: ${color}"></div>
+                                        <div class="category-color <#if color == category.getColor()>category-color-active</#if>" style="background-color: ${color}"></div>
                                     </div>
                             <#else>
                                 <div class="col s2 m1 no-padding">
-                                    <div class="category-color <#if color == model["category"].getColor()>category-color-active</#if>" style="background-color: ${color}"></div>
+                                    <div class="category-color <#if color == category.getColor()>category-color-active</#if>" style="background-color: ${color}"></div>
                                 </div>
                             </#if>
 
                             <#if color?counter == 6 || color?counter == 12>
                                 </div>
                             </#if>
-                        </#list>-
+                        </#list>
                             <#--add custom color picker-->
                             <div class="col s2 m1 no-padding">
-                                <div class="category-color <#if model["customColor"] == model["category"].getColor()>category-color-active</#if>" style="background-color: ${model["customColor"]}">
+                                <div class="category-color <#if customColor == category.getColor()>category-color-active</#if>" style="background-color: ${customColor}">
                                     +
                                 </div>
                             </div>
@@ -81,11 +82,7 @@
             </div>
         </main>
 
-        <!-- notification modal -->
-        <#import "../notification.ftl" as notification>
-        <@notification.notification/>
-
-        <!--  Scripts-->
+        <!-- Scripts-->
         <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
         <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
         <script src="/js/main.js"></script>
diff --git a/src/main/resources/templates/validation.ftl b/src/main/resources/templates/validation.ftl
new file mode 100644
index 000000000..47c87e531
--- /dev/null
+++ b/src/main/resources/templates/validation.ftl
@@ -0,0 +1,8 @@
+<#macro validation fieldName classes="">
+    <#assign locale = localization["tools.Localization"]>
+    <#if error?? && error.hasFieldErrors(fieldName)>
+        class="tooltipped invalid ${classes}" data-position="bottom" data-delay="50" data-tooltip="${locale.getString(error.getFieldError(fieldName).getCode())}"
+    <#else>
+        class="validate ${classes}"
+    </#if>
+</#macro>
\ No newline at end of file
-- 
GitLab