From ca01aa8b9ac6cedbe269270fd536a5f2bf284c59 Mon Sep 17 00:00:00 2001 From: Diego Pino Garcia Date: Mon, 23 Nov 2009 10:47:00 +0100 Subject: [PATCH] ItEr35S14CUAdministracionMateriaisItEr34S14: Added Materials Category tree box to CustomMenuController * List material categories * Add and remove category to category tree --- .../zkoss/ganttz/util/MutableTreeModel.java | 4 + .../materials/daos/IMaterialCategoryDAO.java | 6 + .../materials/daos/MaterialCategoryDAO.java | 14 ++ .../web/common/CustomMenuController.java | 1 + .../web/materials/IMaterialsModel.java | 40 ++++ .../web/materials/MaterialsController.java | 225 ++++++++++++++++++ .../web/materials/MaterialsModel.java | 92 +++++++ .../src/main/webapp/materials/materials.zul | 54 +++++ 8 files changed, 436 insertions(+) create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/materials/IMaterialsModel.java create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsController.java create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsModel.java create mode 100644 navalplanner-webapp/src/main/webapp/materials/materials.zul diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/util/MutableTreeModel.java b/ganttzk/src/main/java/org/zkoss/ganttz/util/MutableTreeModel.java index 8a7844ad4..fec55eedf 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/util/MutableTreeModel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/util/MutableTreeModel.java @@ -320,4 +320,8 @@ public class MutableTreeModel extends AbstractTreeModel { } } + public boolean isEmpty() { + return getChildCount(getRoot()) == 0; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/IMaterialCategoryDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/IMaterialCategoryDAO.java index 36c4e1ee8..ea84d7e82 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/IMaterialCategoryDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/IMaterialCategoryDAO.java @@ -20,6 +20,8 @@ package org.navalplanner.business.materials.daos; +import java.util.List; + import org.navalplanner.business.common.daos.IGenericDAO; import org.navalplanner.business.materials.entities.MaterialCategory; @@ -30,4 +32,8 @@ import org.navalplanner.business.materials.entities.MaterialCategory; */ public interface IMaterialCategoryDAO extends IGenericDAO { + List getAll(); + + List getAllRootMaterialCategories(); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/MaterialCategoryDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/MaterialCategoryDAO.java index bd901e4c4..9136baaf9 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/MaterialCategoryDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/materials/daos/MaterialCategoryDAO.java @@ -20,6 +20,9 @@ package org.navalplanner.business.materials.daos; +import java.util.List; + +import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.daos.GenericDAOHibernate; import org.navalplanner.business.materials.entities.MaterialCategory; import org.springframework.beans.factory.config.BeanDefinition; @@ -36,4 +39,15 @@ import org.springframework.stereotype.Repository; public class MaterialCategoryDAO extends GenericDAOHibernate implements IMaterialCategoryDAO { + @Override + public List getAll() { + return list(MaterialCategory.class); + } + + @SuppressWarnings("unchecked") + @Override + public List getAllRootMaterialCategories() { + return getSession().createCriteria(MaterialCategory.class).add(Restrictions.isNull("parent")).list(); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java index 552ec2627..8fcd9c0b7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java @@ -185,6 +185,7 @@ public class CustomMenuController extends Div implements IMenuItemsRegister { "/resources/criterions/criterions-V2.zul"), subItem(_("Calendars"), "/calendars/calendars.zul"), subItem(_("Label types"), "/labels/labelTypes.zul"), + subItem(_("Materials"), "/materials/materials.zul"), subItem(_("Configuration"), "/common/configuration.zul")); topItem(_("Quality management"), "/", true); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/IMaterialsModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/IMaterialsModel.java new file mode 100644 index 000000000..8f3d67a97 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/IMaterialsModel.java @@ -0,0 +1,40 @@ +/* + * This file is part of ###PROJECT_NAME### + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.materials; + +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.materials.entities.MaterialCategory; +import org.zkoss.ganttz.util.MutableTreeModel; + +/** + * + * @author Diego Pino Garcia + * + */ +public interface IMaterialsModel { + + void addMaterialCategory(MaterialCategory parent, MaterialCategory child) throws ValidationException; + + MutableTreeModel getMaterialCategories(); + + void removeMaterialCategory(MaterialCategory materialCategory); + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsController.java new file mode 100644 index 000000000..8653f25e7 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsController.java @@ -0,0 +1,225 @@ +/* + * This file is part of ###PROJECT_NAME### + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.materials; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.Collection; + +import org.apache.commons.logging.LogFactory; +import org.hibernate.validator.InvalidValue; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.materials.entities.Material; +import org.navalplanner.business.materials.entities.MaterialCategory; +import org.navalplanner.web.common.IMessagesForUser; +import org.navalplanner.web.common.Level; +import org.springframework.beans.factory.annotation.Autowired; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.WrongValueException; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Button; +import org.zkoss.zul.Messagebox; +import org.zkoss.zul.Textbox; +import org.zkoss.zul.Tree; +import org.zkoss.zul.TreeModel; +import org.zkoss.zul.Treecell; +import org.zkoss.zul.Treechildren; +import org.zkoss.zul.Treeitem; +import org.zkoss.zul.TreeitemRenderer; +import org.zkoss.zul.Treerow; + +/** + * Controller for {@link Material} materials + * + * @author Diego Pino García + * + */ +public class MaterialsController extends + GenericForwardComposer { + + private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(MaterialsController.class); + + @Autowired + private IMaterialsModel materialsModel; + + private Tree categoriesTree; + + private Textbox txtCategory; + + private IMessagesForUser messagesForUser; + + private Component messagesContainer; + + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + comp.setVariable("materialsController", this, true); + } + + public TreeModel getMaterialCategories() { + return materialsModel.getMaterialCategories(); + } + + public MaterialCategoryRenderer getMaterialCategoryRenderer() { + return new MaterialCategoryRenderer(); + } + + /** + * Render for criterionsTree + * + * I had to implement a renderer for the Tree, for setting open to tree for + * each treeitem while being rendered. + * + * I tried to do this by iterating through the list of items after setting + * model in doAfterCompose, but I got a ConcurrentModificationException. It + * seems that at that point some other component was using the list of item, + * so it was not possible to modify it. There's not other point where to + * initialize components but doAfterCompose. + * + * Finally, I tried this solution and it works. + * + * @author Diego Pino Garcia + * + */ + private class MaterialCategoryRenderer implements TreeitemRenderer { + + /** + * Copied verbatim from org.zkoss.zul.Tree; + */ + @Override + public void render(Treeitem ti, Object node) throws Exception { + final MaterialCategory materialCategory = (MaterialCategory) node; + + Textbox tb = new Textbox(materialCategory.getName()); + Treecell tc = new Treecell(); + + Treerow tr = null; + ti.setValue(node); + if (ti.getTreerow() == null) { + tr = new Treerow(); + tr.setParent(ti); + ti.setOpen(true); // Expand node + } else { + tr = ti.getTreerow(); + tr.getChildren().clear(); + } + tb.setParent(tc); + tc.setParent(tr); + appendDeleteButton(ti); + } + } + + private void appendDeleteButton(final Treeitem ti) { + final MaterialCategory materialCategory = (MaterialCategory) ti.getValue(); + + Button btnDelete = new Button("", "/common/img/ico_borrar1.png"); + btnDelete.setHoverImage("/common/img/ico_borrar.png"); + btnDelete.setSclass("icono"); + btnDelete.setTooltiptext(_("Delete")); + btnDelete.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + confirmRemove(materialCategory); + } + }); + btnDelete.setDisabled(hasSubcategoriesOrMaterials(materialCategory)); + Treecell tc = new Treecell(); + tc.setParent(ti.getTreerow()); + btnDelete.setParent(tc); + } + + private boolean hasSubcategoriesOrMaterials(MaterialCategory materialCategory) { + return materialCategory.getSubcategories() == null || !materialCategory.getSubcategories().isEmpty(); + } + + public void confirmRemove(MaterialCategory materialCategory) { + + try { + int status = Messagebox.show(_("Confirm deleting {0}. Are you sure?", + materialCategory.getName()), _("Delete"), + Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION); + if (Messagebox.OK == status) { + removeMaterialCategory(materialCategory); + } + } catch (InterruptedException e) { + messagesForUser.showMessage(Level.ERROR, e.getMessage()); + LOG.error(_("Error on showing removing element: ", materialCategory.getId()), e); + } + } + + private void removeMaterialCategory(MaterialCategory materialCategory) { + materialsModel.removeMaterialCategory(materialCategory); + } + + public void addMaterialCategory() { + String categoryName = txtCategory.getValue(); + if (categoryName == null || categoryName.isEmpty()) { + throw new WrongValueException(txtCategory, _("cannot be null or empty")); + } + + final MaterialCategory category = MaterialCategory.create(_(categoryName)); + MaterialCategory parent = null; + final Treeitem treeitem = categoriesTree.getSelectedItem(); + if (treeitem != null) { + parent = (MaterialCategory) treeitem.getValue(); + } + try { + materialsModel.addMaterialCategory(parent, category); + txtCategory.setValue(""); + } catch (ValidationException e) { + for (InvalidValue invalidValue : e.getInvalidValues()) { + Object value = invalidValue.getBean(); + if (value instanceof MaterialCategory) { + MaterialCategory materialCategory = (MaterialCategory) value; + Component comp = findInMaterialCategoryTree(materialCategory); + if (comp != null) { + throw new WrongValueException(comp, invalidValue.getMessage()); + } + } + } + } + } + + private Component findInMaterialCategoryTree(MaterialCategory materialCategory) { + final Treechildren children = categoriesTree.getTreechildren(); + for(Treeitem each: (Collection) children.getItems()) { + final MaterialCategory _materialCategory = (MaterialCategory) each.getValue(); + final Textbox textbox = getMaterialCategoryTextbox(each); + // Clear previous errors + textbox.clearErrorMessage(); + if (_materialCategory.equals(materialCategory)) { + return textbox; + } + } + return null; + } + + private Textbox getMaterialCategoryTextbox(Treeitem treeitem) { + final Treerow tr = treeitem.getTreerow(); + final Treecell tc = (Treecell) tr.getChildren().get(0); + return (Textbox) tc.getChildren().get(0); + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsModel.java new file mode 100644 index 000000000..ddb58ce5a --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/materials/MaterialsModel.java @@ -0,0 +1,92 @@ +package org.navalplanner.web.materials; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.hibernate.validator.InvalidValue; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.materials.daos.IMaterialCategoryDAO; +import org.navalplanner.business.materials.entities.MaterialCategory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.zkoss.ganttz.util.MutableTreeModel; + +@Service +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class MaterialsModel implements IMaterialsModel { + + @Autowired + IMaterialCategoryDAO categoryDAO; + + MutableTreeModel materialCategories = MutableTreeModel.create(MaterialCategory.class); + + private void initializeMaterialCategories() { + final List categories = categoryDAO.getAllRootMaterialCategories(); + for (MaterialCategory materialCategory: categories) { + materialCategories.addToRoot(materialCategory); + addCategories(materialCategory, materialCategory.getSubcategories()); + } + } + + private void addCategories(MaterialCategory materialCategory, Set categories) { + for (MaterialCategory category: categories) { + materialCategories.add(materialCategory, category); + final Set subcategories = category.getSubcategories(); + if (subcategories != null) { + addCategories(category, subcategories); + } + } + } + + @Transactional(readOnly=true) + public MutableTreeModel getMaterialCategories() { + if (materialCategories.isEmpty()) { + initializeMaterialCategories(); + } + return materialCategories; + } + + @Override + public void addMaterialCategory(MaterialCategory parent, MaterialCategory child) throws ValidationException { + Validate.notNull(child); + + final MaterialCategory materialCategory = findMaterialCategory(parent, child); + if (materialCategory != null) { + final InvalidValue invalidValue = new InvalidValue(_("{0} already exists", materialCategory.getName()), + MaterialCategory.class, "name", materialCategory.getName(), materialCategory); + throw new ValidationException(invalidValue); + } + + if (parent == null) { + materialCategories.addToRoot(child); + } else { + materialCategories.add(parent, child); + } + } + + private MaterialCategory findMaterialCategory(final MaterialCategory parent, final MaterialCategory category) { + for (int i = 0; i < materialCategories.getChildCount(parent); i++) { + final MaterialCategory each = materialCategories.getChild(parent, i); + if (equalsMaterialCategory(each, category)) { + return each; + } + } + return null; + } + + private boolean equalsMaterialCategory(MaterialCategory obj1, MaterialCategory obj2) { + return obj1.getName().equals(obj2.getName()); + } + + @Override + public void removeMaterialCategory(MaterialCategory materialCategory) { + materialCategories.remove(materialCategory); + } + +} diff --git a/navalplanner-webapp/src/main/webapp/materials/materials.zul b/navalplanner-webapp/src/main/webapp/materials/materials.zul new file mode 100644 index 000000000..c99fd5d5f --- /dev/null +++ b/navalplanner-webapp/src/main/webapp/materials/materials.zul @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + +