diff --git a/ganttzk-demo-webapp/pom.xml b/ganttzk-demo-webapp/pom.xml
new file mode 100644
index 000000000..2ebd74928
--- /dev/null
+++ b/ganttzk-demo-webapp/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ org.navalplanner
+ navalplanner
+ 1.0.0
+
+ ganttzk-demo-webapp
+ war
+ Naval Planner Web Client Module
+
+
+ ganttzk-demo-webapp
+
+
+
+
+
+ org.springframework
+ spring
+
+
+
+ org.beanshell
+ bsh
+
+
+
+ commons-fileupload
+ commons-fileupload
+
+
+
+ org.zkoss.zk
+ zul
+
+
+ org.zkoss.zk
+ zkplus
+
+
+ org.zkoss.zk
+ zk
+
+
+ org.zkoss.zk
+ zkex
+
+
+
+ org.navalplanner
+ navalplanner-gantt-zk
+
+
+ org.easymock
+ easymock
+
+
+ junit
+ junit
+
+
+ org.hibernate
+ hibernate-validator
+
+
+ javax.servlet
+ servlet-api
+
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/Level.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/Level.java
new file mode 100644
index 000000000..6c5f612e0
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/Level.java
@@ -0,0 +1,5 @@
+package org.navalplanner.web.common;
+
+public enum Level {
+ INFO, WARNING, ERROR;
+}
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/OnlyOneVisible.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/OnlyOneVisible.java
new file mode 100644
index 000000000..fdc22db90
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/OnlyOneVisible.java
@@ -0,0 +1,29 @@
+package org.navalplanner.web.common;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.zkoss.zk.ui.Component;
+
+/**
+ * Utility for enforcing that only one of the supplied component is visible.
+ * @author Óscar González Fernández
+ */
+public class OnlyOneVisible {
+
+ private List components;
+
+ public OnlyOneVisible(Component... components) {
+ this.components = Arrays.asList(components);
+ showOnly(null);
+ }
+
+ public void showOnly(Component component) {
+ for (Component c : components) {
+ if (c != null) {
+ c.setVisible(component != null && c.equals(component));
+ }
+ }
+ }
+
+}
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/Util.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/Util.java
new file mode 100644
index 000000000..c6f6c2fe2
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/Util.java
@@ -0,0 +1,312 @@
+package org.navalplanner.web.common;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import org.zkoss.zk.ui.Component;
+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.event.InputEvent;
+import org.zkoss.zkplus.databind.DataBinder;
+import org.zkoss.zul.Checkbox;
+import org.zkoss.zul.Datebox;
+import org.zkoss.zul.Decimalbox;
+import org.zkoss.zul.Intbox;
+import org.zkoss.zul.Textbox;
+
+/**
+ * Utilities class.
+ *
+ * @author Óscar González Fernández
+ * @author Manuel Rego Casasnovas
+ */
+public class Util {
+
+ public static void reloadBindings(Component... toReload) {
+ for (Component reload : toReload) {
+ DataBinder binder = Util.getBinder(reload);
+ if (binder != null) {
+ binder.loadComponent(reload);
+ }
+ }
+ }
+
+ public static void saveBindings(Component... toReload) {
+ for (Component reload : toReload) {
+ DataBinder binder = Util.getBinder(reload);
+ if (binder != null) {
+ binder.saveComponent(reload);
+ }
+ }
+ }
+
+ public static DataBinder getBinder(Component component) {
+ return (DataBinder) component.getVariable("binder", false);
+ }
+
+ /**
+ * Generic interface to represent a class with a typical get method.
+ *
+ * @author Manuel Rego Casasnovas
+ *
+ * @param
+ * The type of the variable to be returned.
+ */
+ public static interface Getter {
+ /**
+ * Typical get method that returns a variable.
+ *
+ * @return A variable of type .
+ */
+ public T get();
+ }
+
+ /**
+ * Generic interface to represent a class with a typical set method.
+ *
+ * @author Manuel Rego Casasnovas
+ *
+ * @param
+ * The type of the variable to be set.
+ */
+ public static interface Setter {
+ /**
+ * Typical set method to store a variable.
+ *
+ * @param value
+ * A variable of type to be set.
+ */
+ public void set(T value);
+ }
+
+ /**
+ * Binds a {@link Textbox} with a {@link Getter}. The {@link Getter} will be
+ * used to get the value that is going to be showed in the {@link Textbox}.
+ *
+ * @param textBox
+ * The {@link Textbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @return The {@link Textbox} bound
+ */
+ public static Textbox bind(Textbox textBox, Getter getter) {
+ textBox.setValue(getter.get());
+ textBox.setDisabled(true);
+ return textBox;
+ }
+
+ /**
+ * Binds a {@link Textbox} with a {@link Getter}. The {@link Getter} will be
+ * used to get the value that is going to be showed in the {@link Textbox}.
+ * The {@link Setter} will be used to store the value inserted by the user
+ * in the {@link Textbox}.
+ *
+ * @param textBox
+ * The {@link Textbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @param setter
+ * The {@link Setter} interface that will implement a set method.
+ * @return The {@link Textbox} bound
+ */
+ public static Textbox bind(final Textbox textBox,
+ final Getter getter, final Setter setter) {
+ textBox.setValue(getter.get());
+ textBox.addEventListener("onChange", new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ InputEvent newInput = (InputEvent) event;
+ String value = newInput.getValue();
+ setter.set(value);
+ textBox.setValue(getter.get());
+ }
+ });
+ return textBox;
+ }
+
+ /**
+ * Binds a {@link Intbox} with a {@link Getter}. The {@link Getter} will be
+ * used to get the value that is going to be showed in the {@link Intbox}.
+ *
+ * @param intBox
+ * The {@link Intbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @return The {@link Intbox} bound
+ */
+ public static Intbox bind(Intbox intBox, Getter getter) {
+ intBox.setValue(getter.get());
+ intBox.setDisabled(true);
+ return intBox;
+ }
+
+ /**
+ * Binds a {@link Intbox} with a {@link Getter}. The {@link Getter} will be
+ * used to get the value that is going to be showed in the {@link Intbox}.
+ * The {@link Setter} will be used to store the value inserted by the user
+ * in the {@link Intbox}.
+ *
+ * @param intBox
+ * The {@link Intbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @param setter
+ * The {@link Setter} interface that will implement a set method.
+ * @return The {@link Intbox} bound
+ */
+ public static Intbox bind(final Intbox intBox,
+ final Getter getter, final Setter setter) {
+ intBox.setValue(getter.get());
+ intBox.addEventListener("onChange", new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ InputEvent newInput = (InputEvent) event;
+ String value = newInput.getValue();
+ setter.set(Integer.valueOf(value));
+ intBox.setValue(getter.get());
+ }
+ });
+ return intBox;
+ }
+
+ /**
+ * Binds a {@link Datebox} with a {@link Getter}. The {@link Getter} will be
+ * used to get the value that is going to be showed in the {@link Datebox}.
+ *
+ * @param dateBox
+ * The {@link Datebox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @return The {@link Datebox} bound
+ */
+ public static Datebox bind(final Datebox dateBox,
+ final Getter getter) {
+ dateBox.setValue(getter.get());
+ dateBox.setDisabled(true);
+ return dateBox;
+ }
+
+ /**
+ * Binds a {@link Datebox} with a {@link Getter}. The {@link Getter} will be
+ * used to get the value that is going to be showed in the {@link Datebox}.
+ * The {@link Setter} will be used to store the value inserted by the user
+ * in the {@link Datebox}.
+ *
+ * @param dateBox
+ * The {@link Datebox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @param setter
+ * The {@link Setter} interface that will implement a set method.
+ * @return The {@link Datebox} bound
+ */
+ public static Datebox bind(final Datebox dateBox,
+ final Getter getter, final Setter setter) {
+ dateBox.setValue(getter.get());
+ dateBox.addEventListener("onChange", new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ setter.set(dateBox.getValue());
+ dateBox.setValue(getter.get());
+ }
+ });
+ return dateBox;
+ }
+
+ /**
+ * Binds a {@link Decimalbox} with a {@link Getter}. The {@link Getter} will
+ * be used to get the value that is going to be showed in the
+ * {@link Decimalbox}.
+ *
+ * @param decimalBox
+ * The {@link Decimalbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @return The {@link Decimalbox} bound
+ */
+ public static Decimalbox bind(final Decimalbox decimalBox,
+ final Getter getter) {
+ decimalBox.setValue(getter.get());
+ decimalBox.setDisabled(true);
+ return decimalBox;
+ }
+
+ /**
+ * Binds a {@link Decimalbox} with a {@link Getter}. The {@link Getter} will
+ * be used to get the value that is going to be showed in the
+ * {@link Decimalbox}. The {@link Setter} will be used to store the value
+ * inserted by the user in the {@link Decimalbox}.
+ *
+ * @param decimalBox
+ * The {@link Decimalbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @param setter
+ * The {@link Setter} interface that will implement a set method.
+ * @return The {@link Decimalbox} bound
+ */
+ public static Decimalbox bind(final Decimalbox decimalBox,
+ final Getter getter, final Setter setter) {
+ decimalBox.setValue(getter.get());
+ decimalBox.addEventListener("onChange", new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ setter.set(decimalBox.getValue());
+ decimalBox.setValue(getter.get());
+ }
+ });
+ return decimalBox;
+ }
+
+ /**
+ * Binds a {@link Checkbox} with a {@link Getter}. The {@link Getter} will
+ * be used to get the value that is going to be showed in the
+ * {@link Checkbox}.
+ *
+ * @param decimalBox
+ * The {@link Checkbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @return The {@link Checkbox} bound
+ */
+ public static Checkbox bind(final Checkbox checkBox,
+ final Getter getter) {
+ checkBox.setChecked(getter.get());
+ checkBox.setDisabled(true);
+ return checkBox;
+ }
+
+ /**
+ * Binds a {@link Checkbox} with a {@link Getter}. The {@link Getter} will
+ * be used to get the value that is going to be showed in the
+ * {@link Checkbox}. The {@link Setter} will be used to store the value
+ * inserted by the user in the {@link Checkbox}.
+ *
+ * @param decimalBox
+ * The {@link Checkbox} to be bound
+ * @param getter
+ * The {@link Getter} interface that will implement a get method.
+ * @param setter
+ * The {@link Setter} interface that will implement a set method.
+ * @return The {@link Checkbox} bound
+ */
+ public static Checkbox bind(final Checkbox checkBox,
+ final Getter getter, final Setter setter) {
+ checkBox.setChecked(getter.get());
+ checkBox.addEventListener(Events.ON_CHECK, new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ setter.set(checkBox.isChecked());
+ checkBox.setChecked(getter.get());
+ }
+ });
+ return checkBox;
+ }
+
+}
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/components/TwoWaySelector.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/components/TwoWaySelector.java
new file mode 100644
index 000000000..d6c7e5f1a
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/components/TwoWaySelector.java
@@ -0,0 +1,268 @@
+package org.navalplanner.web.common.components;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.navalplanner.web.common.Util;
+import org.zkoss.lang.Objects;
+import org.zkoss.zk.ui.HtmlMacroComponent;
+import org.zkoss.zul.Listbox;
+import org.zkoss.zul.Listcell;
+import org.zkoss.zul.Listitem;
+import org.zkoss.zul.ListitemRenderer;
+
+/**
+ * ZK macro component that shows two {@link Listbox} allowing to move objects
+ * between each other.
+ *
+ * In the {@link Listbox} on the left you will have the assigned objects and in
+ * the right the possible other objects to be assigned.
+ *
+ * Finally it provides methods to get the current assigned and unassigned
+ * objects.
+ *
+ * @author Manuel Rego Casasnovas
+ */
+public class TwoWaySelector extends HtmlMacroComponent {
+
+ /**
+ * A {@link Set} of objects that are assigned (so they're shown on the left
+ * {@link Listbox})
+ */
+ private Set assignedObjects = new HashSet();
+
+ /**
+ * Title for the left {@link Listbox} (where assigned objects are shown)
+ */
+ private String assignedTitle = "Assigned";
+
+ /**
+ * A {@link Set} of objects that are not assigned (so they're shown on the
+ * right {@link Listbox})
+ */
+ private Set unassignedObjects = new HashSet();
+
+ /**
+ * Title for the right {@link Listbox} (where unassigned objects are shown)
+ */
+ private String unassignedTitle = "Unassigned";
+
+ /**
+ * A {@link List} of properties to be shown on the {@link Listbox} for each
+ * object.
+ */
+ private List columns = null;
+
+ /**
+ * {@link ListitemRenderer} that knows how to paint an object according to
+ * the {@link List} stored in the columns attribute. If columns is null then
+ * the object will be rendered as a string.
+ *
+ * @author Manuel Rego Casasnovas
+ */
+ private ListitemRenderer renderer = new ListitemRenderer() {
+ @Override
+ public void render(Listitem item, Object data) throws Exception {
+
+ Class extends Object> klass = data.getClass();
+ Map propertiesByName = getProperties(klass);
+
+ // If a list of attributes is defined
+ if (columns != null) {
+ // For each attribute
+ for (String column : columns) {
+ // Call the method to get the information
+ PropertyDescriptor propertyDescriptor = propertiesByName
+ .get(column);
+ if (propertyDescriptor == null) {
+ throw new RuntimeException(
+ "Unknown attribute '"
+ + column + "' in class " + klass.getName());
+ }
+
+ String label = Objects.toString(propertyDescriptor
+ .getReadMethod().invoke(data));
+
+ // Add a new Listcell
+ item.appendChild(new Listcell(label));
+ }
+ } else { // If the list of attributes is not defined
+ // Render the object as string
+ item.setLabel(Objects.toString(data));
+ }
+
+ item.setValue(data);
+ }
+
+ /**
+ * A {@link Map} that stores the information about the attributes for a
+ * class.
+ *
+ * The information about attributes is stored with another Map where
+ * keys are the properties name and the values the
+ * {@link PropertyDescriptor}.
+ */
+ private Map, Map> propertiesMapsCached = new HashMap, Map>();
+
+ /**
+ * Creates a {@link Map} that relates the properties and their
+ * {@link PropertyDescriptor} from the {@link BeanInfo}.
+ *
+ * @param info
+ * Information about the bean
+ * @return A {@link Map} that relates properties name and
+ * {@link PropertyDescriptor}
+ */
+ private Map buildPropertyDescriptorsMap(
+ BeanInfo info) {
+ PropertyDescriptor[] propertyDescriptors = info
+ .getPropertyDescriptors();
+ Map propertiesByName = new HashMap();
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ propertiesByName.put(propertyDescriptor.getName(),
+ propertyDescriptor);
+ }
+ return propertiesByName;
+ }
+
+ /**
+ * Gets the attributes of a {@link Class} together with the
+ * {@link PropertyDescriptor} of each property.
+ *
+ * @param klass
+ * The {@link Class} to get the properties
+ * @return A {@link Map} that relates properties name and
+ * {@link PropertyDescriptor}
+ * @throws IntrospectionException
+ */
+ private Map getProperties(
+ Class extends Object> klass) throws IntrospectionException {
+ // If it's already cached
+ if (propertiesMapsCached.containsKey(klass)) {
+ return propertiesMapsCached.get(klass);
+ }
+
+ BeanInfo beanInfo = Introspector.getBeanInfo(klass);
+ Map result = buildPropertyDescriptorsMap(beanInfo);
+
+ // Store in cache
+ propertiesMapsCached.put(klass, result);
+
+ return result;
+ }
+ };
+
+ public void setAssignedTitle(String assignedTitle) {
+ if (assignedTitle != null) {
+ this.assignedTitle = assignedTitle;
+ }
+ }
+
+ public String getAssignedTitle() {
+ return assignedTitle;
+ }
+
+ public void setUnassignedTitle(String unassignedTitle) {
+ if (unassignedTitle != null) {
+ this.unassignedTitle = unassignedTitle;
+ }
+ }
+
+ public String getUnassignedTitle() {
+ return unassignedTitle;
+ }
+
+ public void setAssignedObjects(Set assignedObjects) {
+ if (assignedObjects != null) {
+ this.assignedObjects = assignedObjects;
+ }
+ }
+
+ public Set getAssignedObjects() {
+ return assignedObjects;
+ }
+
+ public void setUnassignedObjects(Set unassignedObjects) {
+ if (assignedObjects != null) {
+ this.unassignedObjects = unassignedObjects;
+ }
+ }
+
+ public Set getUnassignedObjects() {
+ return unassignedObjects;
+ }
+
+ /**
+ * Sets the list of attributes to be shown when an object is renderer.
+ *
+ * @param columns
+ * A comma-separated string
+ */
+ public void setColumns(String columns) {
+ if (columns != null) {
+ // Remove white spaces
+ columns = columns.replaceAll("\\s", "");
+
+ if (!columns.isEmpty()) {
+ // Split the string
+ this.columns = Arrays.asList(columns.split(","));
+ }
+ }
+ }
+
+ public List getColumns() {
+ return columns;
+ }
+
+ public ListitemRenderer getRenderer() {
+ return renderer;
+ }
+
+ /**
+ * Assign (move to the left {@link Listbox}) the selected items from the
+ * right {@link Listbox}. And reload both {@link Listbox} in order to
+ * relfect the changes.
+ *
+ * @param unassignedObjectsListbox
+ * The right {@link Listbox}
+ */
+ public void assign(Listbox unassignedObjectsListbox) {
+ Set selectedItems = unassignedObjectsListbox
+ .getSelectedItems();
+ for (Listitem listitem : selectedItems) {
+ Object value = listitem.getValue();
+ unassignedObjects.remove(value);
+ assignedObjects.add(value);
+ }
+ Util.reloadBindings(unassignedObjectsListbox.getParent());
+ Util.saveBindings(this);
+ }
+
+ /**
+ * Unassign (move to the rigth {@link Listbox}) the selected items from the
+ * left {@link Listbox}. And reload both {@link Listbox} in order to relfect
+ * the changes.
+ *
+ * @param assignedObjectsListbox
+ * The left {@link Listbox}
+ */
+ public void unassign(Listbox assignedObjectsListbox) {
+ Set selectedItems = assignedObjectsListbox.getSelectedItems();
+ for (Listitem listitem : selectedItems) {
+ Object value = listitem.getValue();
+ assignedObjects.remove(value);
+ unassignedObjects.add(value);
+ }
+ Util.reloadBindings(assignedObjectsListbox.getParent());
+ Util.saveBindings(this);
+ }
+
+}
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/typeconverters/DateConverter.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/typeconverters/DateConverter.java
new file mode 100644
index 000000000..208e28c54
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/common/typeconverters/DateConverter.java
@@ -0,0 +1,26 @@
+package org.navalplanner.web.common.typeconverters;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zkplus.databind.TypeConverter;
+
+/**
+ * Converter for the type java.util.Date
+ *
+ * @author Diego Pino Garcia
+ *
+ */
+public class DateConverter implements TypeConverter {
+
+ @Override
+ public Object coerceToBean(Object arg0, Component arg1) {
+ return null;
+ }
+
+ @Override
+ public Object coerceToUi(Object object, Component component) {
+ return (new SimpleDateFormat("dd/MM/yyyy")).format((Date) object);
+ }
+}
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/error/PageForErrorOnEvent.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/error/PageForErrorOnEvent.java
new file mode 100644
index 000000000..91c042e33
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/error/PageForErrorOnEvent.java
@@ -0,0 +1,48 @@
+package org.navalplanner.web.error;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.Executions;
+import org.zkoss.zk.ui.util.GenericForwardComposer;
+
+public class PageForErrorOnEvent extends GenericForwardComposer {
+
+ private static final Log LOG = LogFactory.getLog(PageForErrorOnEvent.class);
+
+ private Component modalWindow;
+
+ @Override
+ public void doAfterCompose(Component comp) throws Exception {
+ super.doAfterCompose(comp);
+ logError();
+ modalWindow = comp;
+
+ }
+
+ private void logError() {
+ Throwable exception = (Throwable) Executions.getCurrent().getAttribute(
+ "javax.servlet.error.exception");
+ String errorMessage = (String) Executions.getCurrent().getAttribute(
+ "javax.servlet.error.message");
+ LOG.error(errorMessage, exception);
+ }
+
+ public void onClick$continueWorking() {
+ modalWindow.detach();
+ }
+
+ public void onClick$reload() {
+ Executions.sendRedirect(null);
+ }
+
+ public void onClick$quitSession() {
+ HttpServletRequest nativeRequest = (HttpServletRequest) Executions
+ .getCurrent().getNativeRequest();
+ nativeRequest.getSession().invalidate();
+ Executions.sendRedirect("/");
+ }
+
+}
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/DataForPlanner.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/DataForPlanner.java
new file mode 100644
index 000000000..642b269ca
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/DataForPlanner.java
@@ -0,0 +1,206 @@
+package org.navalplanner.web.planner;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import org.zkoss.ganttz.TaskEditFormComposer;
+import org.zkoss.ganttz.adapters.AutoAdapter;
+import org.zkoss.ganttz.adapters.DomainDependency;
+import org.zkoss.ganttz.adapters.IStructureNavigator;
+import org.zkoss.ganttz.adapters.PlannerConfiguration;
+import org.zkoss.ganttz.data.DefaultFundamentalProperties;
+import org.zkoss.ganttz.data.DependencyType;
+import org.zkoss.ganttz.data.GanttDiagramGraph;
+import org.zkoss.ganttz.data.ITaskFundamentalProperties;
+import org.zkoss.ganttz.data.Task;
+import org.zkoss.ganttz.data.TaskContainer;
+import org.zkoss.ganttz.data.TaskLeaf;
+import org.zkoss.ganttz.extensions.ICommand;
+import org.zkoss.ganttz.extensions.ICommandOnTask;
+import org.zkoss.ganttz.extensions.IContext;
+import org.zkoss.ganttz.extensions.IContextWithPlannerTask;
+
+/**
+ * Some test data for planner
+ * @author Óscar González Fernández
+ */
+public class DataForPlanner {
+
+ private TaskEditFormComposer taskEditForm = new TaskEditFormComposer();
+
+ public DataForPlanner() {
+
+ }
+
+ public GanttDiagramGraph getEmpty() {
+ return new GanttDiagramGraph();
+ }
+
+ private PlannerConfiguration addCommands(
+ PlannerConfiguration configuration) {
+ configuration
+ .addGlobalCommand(new ICommand() {
+
+ @Override
+ public String getName() {
+ return "Add Task";
+ }
+
+ @Override
+ public void doAction(
+ IContext context) {
+ addNewTask(context);
+ }
+ });
+ configuration
+ .setGoingDownInLastArrowCommand(new ICommand() {
+
+ @Override
+ public void doAction(
+ IContext context) {
+ addNewTask(context);
+ }
+
+ @Override
+ public String getName() {
+ return "";
+ }
+ });
+ configuration
+ .addCommandOnTask(new ICommandOnTask() {
+ @Override
+ public void doAction(
+ IContextWithPlannerTask context,
+ ITaskFundamentalProperties task) {
+ context.remove(task);
+ }
+
+ @Override
+ public String getName() {
+ return "Remove";
+ }
+
+ });
+ configuration.setEditTaskCommand(new ICommandOnTask() {
+
+ @Override
+ public void doAction(
+ IContextWithPlannerTask context,
+ ITaskFundamentalProperties task) {
+ taskEditForm.showEditFormFor(context.getRelativeTo(),
+ context.getTask());
+ }
+
+ @Override
+ public String getName() {
+ return "";
+ }
+ });
+ return configuration;
+ }
+
+ public PlannerConfiguration getLightLoad() {
+ return addCommands(getModelWith(20));
+ }
+
+ public PlannerConfiguration getMediumLoad() {
+ return addCommands(getModelWith(300));
+ }
+
+ public PlannerConfiguration getHighLoad() {
+ return addCommands(getModelWith(500));
+ }
+
+ private PlannerConfiguration getModelWith(
+ int tasksToCreate) {
+ List list = new ArrayList();
+ Date now = new Date();
+ Date end = twoMonthsLater(now);
+ final ITaskFundamentalProperties container = createTask("container",
+ now, end);
+ final List containerChildren = new ArrayList();
+ final ITaskFundamentalProperties child1 = createTask("child 1", now,
+ end);
+ containerChildren.add(child1);
+ final DefaultFundamentalProperties child2 = createTask("another", now,
+ end);
+ containerChildren.add(child2);
+ list.add(container);
+ final ITaskFundamentalProperties first = createTask("tarefa1", now, end);
+ final ITaskFundamentalProperties second = createTask("tarefa2", now,
+ end);
+ list.add(first);
+ list.add(second);
+ for (int i = 2; i < tasksToCreate - 3; i++) {
+ String name = "tarefa " + (i + 1);
+ ITaskFundamentalProperties task = createTask(name, now, end);
+ list.add(task);
+ }
+ IStructureNavigator navigator = new IStructureNavigator() {
+
+ @Override
+ public List getChildren(
+ ITaskFundamentalProperties object) {
+ if (object == container)
+ return containerChildren;
+ return new ArrayList();
+ }
+
+ @Override
+ public boolean isLeaf(ITaskFundamentalProperties object) {
+ return object != container;
+ }
+ };
+ return new PlannerConfiguration(
+ new AutoAdapter() {
+ @Override
+ public List> getOutcomingDependencies(
+ ITaskFundamentalProperties object) {
+ List> result = new ArrayList>();
+ if (child1 == object) {
+ result.add(DomainDependency.createDependency(
+ child1, child2, DependencyType.END_START));
+ } else if (first == object) {
+ result.add(DomainDependency.createDependency(first,
+ second, DependencyType.END_START));
+ }
+ return result;
+ }
+ }, navigator, list);
+ }
+
+ private TaskContainer createContainer(String name, Date start, Date end) {
+ TaskContainer container = new TaskContainer();
+ container.setBeginDate(start);
+ container.setEndDate(end);
+ container.setName(name);
+ return container;
+ }
+
+ private DefaultFundamentalProperties createTask(String name, Date now,
+ Date end) {
+ return new DefaultFundamentalProperties(name, end, end.getTime()
+ - now.getTime(), "bla");
+ }
+
+ private void addNewTask(IContext context) {
+ Task newTask = new TaskLeaf();
+ newTask.setName("Nova Tarefa");
+ newTask.setBeginDate(new Date());
+ newTask.setEndDate(twoMonthsLater(newTask.getBeginDate()));
+ context.add(newTask);
+ }
+
+ private static Date twoMonthsLater(Date now) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(now);
+ calendar.add(Calendar.MONTH, 2);
+ return calendar.getTime();
+ }
+
+ public TaskEditFormComposer getTaskEditForm() {
+ return taskEditForm;
+ }
+}
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/ShareBean.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/ShareBean.java
new file mode 100644
index 000000000..c650fad52
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/ShareBean.java
@@ -0,0 +1,68 @@
+package org.navalplanner.web.planner;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.Validate;
+
+public class ShareBean {
+
+ public static int[] toHours(ShareBean... shares) {
+ Validate.noNullElements(shares);
+ int[] result = new int[shares.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = shares[i].getHours();
+ }
+ return result;
+ }
+
+ public static int sum(Collection extends ShareBean> shareBeans) {
+ Validate.noNullElements(shareBeans);
+ int result = 0;
+ for (ShareBean shareBean : shareBeans) {
+ result += shareBean.getHours();
+ }
+ return result;
+ }
+
+ public static List toShareBeans(String name, int[] hours) {
+ ArrayList result = new ArrayList();
+ for (int i = 0; i < hours.length; i++) {
+ ShareBean s = new ShareBean();
+ s.setName(name + "." + (i + 1));
+ s.setHours(hours[i]);
+ result.add(s);
+ }
+ return result;
+ }
+
+ private String name;
+
+ private Integer hours;
+
+ public ShareBean() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ if (StringUtils.isEmpty(name))
+ return;
+ this.name = name;
+ }
+
+ public Integer getHours() {
+ return hours;
+ }
+
+ public void setHours(Integer share) {
+ if (share == null || share <= 0)
+ return;
+ this.hours = share;
+ }
+
+}
diff --git a/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/SplittingController.java b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/SplittingController.java
new file mode 100644
index 000000000..bc0810446
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/java/org/navalplanner/web/planner/SplittingController.java
@@ -0,0 +1,81 @@
+package org.navalplanner.web.planner;
+
+import java.util.List;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.zkoss.zk.ui.WrongValueException;
+import org.zkoss.zk.ui.util.Clients;
+import org.zkoss.zk.ui.util.GenericForwardComposer;
+import org.zkoss.zul.Label;
+import org.zkoss.zul.SimpleListModel;
+import org.zkoss.zul.Window;
+import org.zkoss.zul.api.Grid;
+
+@Component
+@Scope(BeanDefinition.SCOPE_PROTOTYPE)
+public class SplittingController extends GenericForwardComposer {
+
+ private IActionOnOk curentAction;
+ private Window window;
+
+ private Grid sharesListing;
+
+ private Label totalHoursLabel;
+ private List sharesList;
+ private Integer totalHours;
+
+ public interface IActionOnOk {
+ public void doOkAction(ShareBean[] shares);
+ }
+
+ public void show(List initialSharesList, Integer totalHours,
+ IActionOnOk ok) {
+ this.sharesList = initialSharesList;
+ this.totalHours = totalHours;
+ this.curentAction = ok;
+ this.totalHoursLabel.setValue(totalHours + "");
+ this.sharesListing.setModel(new SimpleListModel(initialSharesList));
+ showWindow();
+ }
+
+ public void onClick$splitOk() {
+ checkSumIsEqualToTotal();
+ Clients.closeErrorBox(totalHoursLabel);
+ hideWindow();
+ curentAction.doOkAction(this.sharesList.toArray(new ShareBean[0]));
+ }
+
+ private void checkSumIsEqualToTotal() {
+ int sum = ShareBean.sum(sharesList);
+ if (sum != totalHours) {
+ throw new WrongValueException(totalHoursLabel,
+ "the sum is not equal: " + sum);
+ }
+ }
+
+ public void onClick$splitCancel() {
+ hideWindow();
+ }
+
+ private void showWindow() {
+ try {
+ window.setMode("modal");
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void hideWindow() {
+ window.setVisible(false);
+ }
+
+ @Override
+ public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception {
+ super.doAfterCompose(comp);
+ window = (Window) comp;
+
+ }
+
+}
diff --git a/ganttzk-demo-webapp/src/main/resources/metainfo/zk/lang-addon.xml b/ganttzk-demo-webapp/src/main/resources/metainfo/zk/lang-addon.xml
new file mode 100755
index 000000000..ad28e0255
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/resources/metainfo/zk/lang-addon.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ navalplanner-webapp
+ xul/html
+
+
+ twowayselector
+ org.navalplanner.web.common.components.TwoWaySelector
+ /common/components/twowayselector.zul
+
+
+
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/resources/navalplanner-webapp-spring-config.xml b/ganttzk-demo-webapp/src/main/resources/navalplanner-webapp-spring-config.xml
new file mode 100644
index 000000000..acf049dd7
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/resources/navalplanner-webapp-spring-config.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/META-INF/context.xml b/ganttzk-demo-webapp/src/main/webapp/META-INF/context.xml
new file mode 100644
index 000000000..2811d9186
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/META-INF/context.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/WEB-INF/i3-label.properties b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/i3-label.properties
new file mode 100644
index 000000000..e86778d96
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/i3-label.properties
@@ -0,0 +1,46 @@
+task.name=Nome
+task.start=Comezo
+task.end=Final
+task.notes=Notas
+task.ok=Aceptar
+listdetails.add_task=Engadir
+task.new_task_name=Nova Tarefa
+DETAIL_ONE=Detalle 1
+DETAIL_TWO=Detalle 2
+DETAIL_THREE=Detalle 3
+DETAIL_FOUR=Detalle 4
+mainmenu.new=Novo
+mainmenu.open=Abrir
+mainmenu.save=Gardar
+mainmenu.project=Proxecto
+mainmenu.exit=Saír
+mainmenu.resources=Recursos
+mainmenu.list_workers=Lista traballadores
+mainmenu.add_resources=Engadir recurso
+mainmenu.manage_resources=Administrar recursos
+mainmenu.check_plannification=Revisar conflitos de planificación
+mainmenu.plannification=Planificación
+mainmenu.add_task=Engadir tarefa
+mainmenu.manage_tasks=Administrar tarefas
+mainmenu.set_complection_data=Establecer nivel de complección
+mainmenu.add_dependency=Engadir dependencia
+mainmenu.manage_dependencies=Administrar dependencias
+mainmenu.help=Axuda
+mainmenu.about=Acerca de
+mainmenu.aclunaga=Aclunaga
+mainmenu.manage_criterions=Administrar criterios
+mainmenu.orders=Pedidos
+mainmenu.list_orders=Lista de pedidos
+mainmenu.company_overview=Vista de empresa
+mainmenu.plannifications_list=Listado de planificacións
+mainmenu.manage_machines=Xestionar máquinas
+mainmenu.activity_work_types=Tipos de actividad de traballo
+mainmenu.models=Modelos
+mainmenu.work_reports=Partes de traballo
+mainmenu.work_report_list=Listado de partes de traballo
+mainmenu.work_report_types=Tipos de partes de traballo
+mainmenu.work_report_import=Importación de partes de traballo
+mainmenu.administration=Administración
+mainmenu.manage_users=Xestionar usuarios e permisos
+mainmenu.quality_management=Xestión da calidade
+mainmenu.manageAdvancesTypes=Xestionar tipos de avance
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/webapp/WEB-INF/i3-label_en_US.properties b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/i3-label_en_US.properties
new file mode 100644
index 000000000..be5fc1657
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/i3-label_en_US.properties
@@ -0,0 +1,46 @@
+task.name=Name
+task.start=Start
+task.end=End
+task.notes=Notes
+task.ok=Ok
+listdetails.add_task=Add
+task.new_task_name=New Task
+DETAIL_ONE=Detail 1
+DETAIL_TWO=Detail 2
+DETAIL_THREE=Detail 3
+DETAIL_FOUR=Detail 4
+mainmenu.new=New
+mainmenu.open=Open
+mainmenu.save=Save
+mainmenu.project=Project
+mainmenu.exit=Exit
+mainmenu.resources=Resources
+mainmenu.list_workers=Workers list
+mainmenu.add_resources=Add resource
+mainmenu.manage_resources=Manage resources
+mainmenu.check_plannification=Check for plannification conflicts
+mainmenu.plannification=Plannification
+mainmenu.add_task=Add task
+mainmenu.manage_tasks=Manage tasks
+mainmenu.set_complection_data=Set complection data
+mainmenu.add_dependency=Add dependency
+mainmenu.manage_dependencies=Manage dependences
+mainmenu.help=Help
+mainmenu.about=About
+mainmenu.aclunaga=Aclunaga
+mainmenu.manage_criterions=Manage criterions
+mainmenu.orders=Orders
+mainmenu.list_orders=Orders list
+mainmenu.company_overview=Company overview
+mainmenu.plannifications_list=Plannifications list
+mainmenu.manage_machines=Manage machines
+mainmenu.activity_work_types=Activity work types
+mainmenu.models=Models
+mainmenu.work_reports=Work reports
+mainmenu.work_report_list=Work report list
+mainmenu.work_report_types=Work report types
+mainmenu.work_report_import=Work report importation
+mainmenu.administration=Administration
+mainmenu.manage_users=Manage users and permissions
+mainmenu.quality_management=Quality management
+mainmenu.manageAdvancesTypes=Manage Advance types
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/webapp/WEB-INF/web.xml b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000..2e3200426
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,105 @@
+
+
+ ganttzk-demo-webapp
+
+
+
+ contextConfigLocation
+ classpath*:/navalplanner-override-spring-config.xml
+
+
+
+
+
+
+
+ dspLoader
+ org.zkoss.web.servlet.dsp.InterpreterServlet
+
+
+ dspLoader
+ *.dsp
+
+
+
+
+
+
+ Used to cleanup when a session is destroyed
+ ZK Session Cleaner
+ org.zkoss.zk.ui.http.HttpSessionListener
+
+
+
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+ org.springframework.web.context.request.RequestContextListener
+
+
+
+
+
+
+
+
+ ZK loader for ZUML pages
+ zkLoader
+ org.zkoss.zk.ui.http.DHtmlLayoutServlet
+
+
+ update-uri
+ /zkau
+
+ 1
+
+
+ zkLoader
+ *.zul
+
+
+ zkLoader
+ *.zhtml
+
+
+ The asynchronous update engine for ZK
+ auEngine
+ org.zkoss.zk.au.http.DHtmlUpdateServlet
+
+
+ auEngine
+ /zkau/*
+
+
+
+
+ /planner/main.zul
+
+
+
+ java.lang.Throwable
+ /common/error.zul
+
+
+
+ 404
+ /common/page_not_found.zul
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/WEB-INF/zk.xml b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/zk.xml
new file mode 100644
index 000000000..d0ed26c16
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/WEB-INF/zk.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ java.lang.Throwable
+
+ /common/event_error.zul
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/components/twowayselector.zul b/ganttzk-demo-webapp/src/main/webapp/common/components/twowayselector.zul
new file mode 100644
index 000000000..f79bb275e
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/components/twowayselector.zul
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/css/login_v01.css b/ganttzk-demo-webapp/src/main/webapp/common/css/login_v01.css
new file mode 100644
index 000000000..f4d87e63a
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/css/login_v01.css
@@ -0,0 +1,46 @@
+@charset "utf-8";
+body {
+
+}
+
+
+.identificacion {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 18px;
+ color: #0081C3;
+ text-decoration: none;
+ padding-top: 20px;
+ text-align: center;
+ padding-bottom: 5px;
+}
+
+.entrar {
+ color:#333333;
+ font-size:12px;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ border: 1px solid #0081C3;
+ background-color: #C2E1F3;
+ width: 150px;
+ background-image: url(../img/flechitas.gif);
+ height: 25px;
+}
+.usuario_clave {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: #333333;
+ text-decoration: none;
+ padding-top: 5px;
+ text-align: center;
+}
+.campotexto {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: #666666;
+ text-decoration: none;
+ border: 1px solid #CCCCCC;
+ padding-left: 4px;
+}
+.fondo_identificacion {
+ background-image: url(../img/degradado_azul.gif);
+ background-repeat: repeat-x;
+}
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/css/navalpro_v01.css b/ganttzk-demo-webapp/src/main/webapp/common/css/navalpro_v01.css
new file mode 100644
index 000000000..9b1ee2620
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/css/navalpro_v01.css
@@ -0,0 +1,89 @@
+body {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-right: 0px;
+ margin-left: 0px;
+}
+.menuup {
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ text-decoration: none;
+ background-image: url(../img/pestana1.gif);
+ font-size: 11px;
+ color: #0081C2;
+ display: block;
+ padding-top: 10px;
+ padding-left: 6px;
+ width: 125px;
+ font-weight: bold;
+ background-repeat: no-repeat;
+ background-position: -4px 0px;
+ padding-bottom: 5px;
+}
+a.menuup:hover {
+ background-image: url(../img/pestana_sobre.gif);
+}
+
+.sub_menu {
+ font-family: Tahoma, Arial, Helvetica, sans-serif, Tahoma;
+ font-size: 11px;
+ color: #EDF2F7;
+ text-decoration: none;
+ display: block;
+ line-height: 22px;
+ padding-right: 3px;
+ padding-left: 3px;
+}
+a.sub_menu:hover {
+ color: #FFFFFF;
+ text-decoration: underline;
+}
+.menuup_activa {
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ text-decoration: none;
+ font-size: 11px;
+ color: #FFFFFF;
+ display: block;
+ background-image: url(../img/pestana_activa.gif);
+ width: 125px;
+ padding-top: 10px;
+ padding-bottom: 5px;
+ padding-left: 6px;
+ font-weight: bold;
+ background-repeat: no-repeat;
+ background-position: -4px 0px;
+}
+.usuario {
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ text-decoration: none;
+ text-align: right;
+ padding-right: 15px;
+}
+.cerrar_sesion {
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ text-decoration: underline;
+ text-align: right;
+ padding-right: 15px;
+ color: #009900;
+ display: block;
+}
+.ruta {
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-size: 11px;
+ color: #3B89B7;
+ text-decoration: none;
+}
+.fondo_cabecera {
+ background-image: url(../img/cabecera.jpg);
+ background-position: right top;
+ background-repeat: no-repeat;
+}
+.migas_linea {
+ border-top-width: 1px;
+ border-bottom-width: 1px;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-top-color: #DCEEF9;
+ border-bottom-color: #DCEEF9;
+}
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/css/navalpro_zk.css b/ganttzk-demo-webapp/src/main/webapp/common/css/navalpro_zk.css
new file mode 100644
index 000000000..337848ea8
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/css/navalpro_zk.css
@@ -0,0 +1,117 @@
+.logo {
+ background-image: url("../img/v3/blue_ga.jpg");
+ height: 50px;
+ width: 300px;
+ height: 100px;
+ float: left;
+ clear: both;
+ position: absolute;
+}
+
+.headings { /* height:90px; */
+ height: 90px;
+}
+
+table {
+ margin: 0px;
+ padding: 0px;
+ border: 0px;
+}
+
+.errorbox {
+ margin: 40px;
+ padding: 40px;
+ border: solid 1px red;
+}
+
+/* Forms */
+.z-button-tl,.z-button-tm,z-button-tr,.z-button-cl,.z-button-cr,.z-button-bl,.z-button-bm,z-button-br
+ {
+ display: none;
+}
+
+.zk .z-button-cm,.z-button-cm {
+ color: #007bbe;
+ background-color: #FFFFFF;
+ background-image: none;
+ border: 2px solid #007bbe;
+}
+
+.zk .z-button,.zk .z-button-br,.zk .z-button-tr {
+ background-image: none;
+}
+
+.z-button-tr {
+ background-image: none !important;
+}
+
+.z-button-br {
+ background-image: none !important;
+}
+
+.footer {
+ clear: both;
+ margin: 20px;
+ font-size: 12px;
+}
+
+/* ------------- order element tree ------------- */
+.orderTree input {
+ height: 18px;
+ border-bottom: 0px;
+ font-size: 12px;
+}
+
+/* Allows tasknumber span to have fixed width */
+.orderTree .tasknumber {
+ display: inline-block;
+}
+
+/* These constants may be reviewed when testing with 3 digit tasknumbers */
+.orderTree .depth_1 input {
+ width: 470px !important;
+}
+
+.orderTree .depth_2 input {
+ width: 440px !important; /* prev - 30 px */
+}
+
+.orderTree .depth_3 input {
+ width: 410px !important; /* prev - 30 px */
+}
+
+.orderTree .depth_4 input {
+ width: 380px !important; /* prev - 30 px */
+}
+
+.orderTree .depth_5 input {
+ width: 350px !important; /* prev - 30 px */
+}
+
+.orderTree .depth_6 input {
+ width: 320px !important; /* prev - 30 px */
+}
+
+.orderTree .depth_1 .tasknumber {
+ width: 25px;
+}
+
+.orderTree .depth_2 .tasknumber {
+ width: 37px; /* prev + 12 px */
+}
+
+.orderTree .depth_3 .tasknumber {
+ width: 49px; /* prev + 12 px */
+}
+
+.orderTree .depth_4 .tasknumber {
+ width: 61px; /* prev + 12 px */
+}
+
+.orderTree .depth_5 .tasknumber {
+ width: 73px; /* prev + 12 px */
+}
+
+.orderTree .depth_6 .tasknumber {
+ width: 85px; /* prev + 12 px */
+}
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/error.jsp b/ganttzk-demo-webapp/src/main/webapp/common/error.jsp
new file mode 100644
index 000000000..a3ba92060
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/error.jsp
@@ -0,0 +1,12 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+
+
+
+
+Insert title here
+
+
+
+
+
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/error.zul b/ganttzk-demo-webapp/src/main/webapp/common/error.zul
new file mode 100644
index 000000000..0f6972c9b
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/error.zul
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+ Prodúxose un erro na execución:
+ "${requestScope['javax.servlet.error.message']}". O erro
+ gardouse e procurarase arreglalo no menor tempo posible.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/event_error.zul b/ganttzk-demo-webapp/src/main/webapp/common/event_error.zul
new file mode 100644
index 000000000..2e9b46a5f
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/event_error.zul
@@ -0,0 +1,14 @@
+
+
+ Prodúxose un erro na execución:
+ "${requestScope['javax.servlet.error.message']}". O erro
+ gardouse e procurarase arreglalo no menor tempo posible.
+
+
+
+
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/axuda.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/axuda.gif
new file mode 100644
index 000000000..f9e71a752
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/axuda.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/degradado_azul.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/degradado_azul.gif
new file mode 100644
index 000000000..d18778ee2
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/degradado_azul.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/flechitas.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/flechitas.gif
new file mode 100644
index 000000000..b2725099a
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/flechitas.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/ganttzk-demo.png b/ganttzk-demo-webapp/src/main/webapp/common/img/ganttzk-demo.png
new file mode 100644
index 000000000..f25696615
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/ganttzk-demo.png differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/linea_down.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/linea_down.gif
new file mode 100644
index 000000000..05c12041b
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/linea_down.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/linea_pie_login.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/linea_pie_login.gif
new file mode 100644
index 000000000..db7e1c892
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/linea_pie_login.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/logos_aplicacion.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/logos_aplicacion.gif
new file mode 100644
index 000000000..25b743bd3
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/logos_aplicacion.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/migas_separacion.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/migas_separacion.gif
new file mode 100644
index 000000000..78808e4ee
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/migas_separacion.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/pestana1.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/pestana1.gif
new file mode 100644
index 000000000..6209c5f50
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/pestana1.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/pestana_activa.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/pestana_activa.gif
new file mode 100644
index 000000000..6817b7bed
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/pestana_activa.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/pestana_sobre.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/pestana_sobre.gif
new file mode 100644
index 000000000..9ee16e794
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/pestana_sobre.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/img/sub_separacion.gif b/ganttzk-demo-webapp/src/main/webapp/common/img/sub_separacion.gif
new file mode 100644
index 000000000..35ea03455
Binary files /dev/null and b/ganttzk-demo-webapp/src/main/webapp/common/img/sub_separacion.gif differ
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/layout/template_ganttzk_demo.zul b/ganttzk-demo-webapp/src/main/webapp/common/layout/template_ganttzk_demo.zul
new file mode 100644
index 000000000..a7819a2f4
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/layout/template_ganttzk_demo.zul
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ about
+ ganttZK
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/common/page_not_found.zul b/ganttzk-demo-webapp/src/main/webapp/common/page_not_found.zul
new file mode 100644
index 000000000..99a392ea4
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/common/page_not_found.zul
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ A páxina que está solicitando non existe.
+ Se introduciu a dirección directamente na barra de navegación
+ do navegador revísea ou pulse na seguinte ligazón para ir á páxina
+ inicial: Ir a inicio
+ Se chegou a esta páxina dende outra páxina do portal
+ rogámoslle nolo notifique para que sexa subsanado no menor
+ intervalo de tempo posible.
+ Desculpe as molestias.
+
+
+
diff --git a/ganttzk-demo-webapp/src/main/webapp/planner/css/ganttzk.css b/ganttzk-demo-webapp/src/main/webapp/planner/css/ganttzk.css
new file mode 100644
index 000000000..d649bfd7b
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/planner/css/ganttzk.css
@@ -0,0 +1,335 @@
+/*
+* ganttz.css Ganttz specific styles
+* /
+
+The next constants are used within the planner styling:
+
+Ganntz.ListdetailsWidth = 280
+
+
+zkTasklist.HEIGHT_PER_ROW = 15
+zkTasklist.HEIGHT_TIME_TRACKER = 120
+
+zkTasklist.SCROLLBAR_WIDTH = 15
+
+zkTasklist.SCROLL_CONTAINER_INITIAL_HEIGHT = 500
+zkTasklist.SCROLL_CONTAINER_INITIAL_WIDTH = 600
+
+zkTasklist.GANTT_PANEL_LEFT = 300
+
+*/
+
+/* -------------- Listdetails -------------- */
+
+/* External listdetails box */
+.listdetails {
+ width:280px; /* Ganntz.ListdetailsWidth */
+ float:left;
+ margin-top: 0px;
+/* border-bottom: 1px solid #86A4BE; */
+ font-size:10px !important;
+ margin-top:0px;
+}
+
+#listdetails_container {
+ float:left;
+ height:500px; /* zkTasklist.SCROLL_CONTAINER_INITIAL_HEIGHT */
+ position:relative;
+ top:27px;
+ overflow-y: hidden;
+ border-bottom: 1px solid #86A4BE;
+ border-right: 1px solid #86A4BE;
+}
+
+.listdetails img {
+ display:none;
+}
+
+#listdetails_container td {
+ border-bottom:1px solid #86A4BE;
+ border-left:1px solid #86A4BE;
+ border:0px;
+}
+
+#listdetails_container td {
+ padding:0px;
+}
+
+.listdetails input {
+ width: 65px;
+ font-size:10px !important;
+ border-bottom:0px;
+ border-right:0px;
+ height:17px;
+}
+
+#listdetails_container .z-datebox-inp,
+#listdetails_container div.z-tree-col-cnt {
+ font-family:"Verdana,Tahoma,Arial,Helvetica,sans-serif";
+ font-size:10px !important;
+ border-bottom:0px;
+ border-right:0px;
+}
+
+.depth_1 .task_title {
+ width: 121px !important;
+}
+.depth_2 .task_title {
+ width: 104px !important;
+}
+.depth_3 .task_title {
+ width: 85px;
+}
+.depth_4 .task_title {
+ width: 64px;
+}
+
+
+.taskdetail_grid table {
+ height:30px;
+ width:285px; /* Ganntz.ListdetailsWidth */
+}
+
+
+
+#listtasks {
+ position:relative;
+ width:600px;
+ top:0px;
+}
+
+/* Task box properties */
+.box {
+ border: 1px solid;
+ text-align:center;
+ vertical-align: middle;
+ z-index:10;
+ cursor: pointer;
+ cursor: hand;
+}
+
+/* Task lane properties */
+.row {
+ height: 9px; /* 19 */
+ border-bottom: dotted 1px #CCCCCC;
+ margin-bottom: 10px;
+ margin-top: 10px;
+ width: 10000px; /* Defined to be larger than the maximum scroll_inner_x */
+}
+
+
+/* -------------- Dependencies -------------- */
+#listdependencies {
+ position:relative;
+ width:400px;
+ float:left;
+ top:0px;
+}
+
+.dependence {
+ z-index:1;
+ position: absolute;
+}
+
+.end, .start, .mid, .arrow {
+ position:absolute;
+ padding:4px;
+ cursor: crosshair;
+}
+
+.end, .start {
+ height:1px;
+}
+
+.mid {
+ width:1px;
+}
+
+.completion {
+ display: none;
+ width: 30%;
+ margin-top:0px;
+ height: 10px;
+ background-color: #FFCC99;
+ z-index:5;
+ border:0px;
+}
+
+.row span {
+ display:none;
+ position:relative;
+ z-index:5;
+ color:#BBBBBB;
+ white-space:nowrap;
+}
+
+/* -------------- TaskGroup -------------- */
+.taskgroup_start {
+ background-image: url("/ganttzk-demo-webapp/zkau/web/ganttz/img/group_left.png");
+ height: 10px;
+ width: 10px;
+ float:left;
+}
+
+.taskgroup_end {
+ background-image: url("/ganttzk-demo-webapp/zkau/web/ganttz/img/group_right.png");
+ height: 10px;
+ width: 10px;
+ float:right;
+}
+
+.taskgroup, .row .expanded {
+ border-top: solid black 2px;
+ border-bottom: 0px;
+ border-left: 0px;
+ border-right: 0px;
+ background-color: transparent !important;
+}
+
+.row .closed {
+ border-top: solid black 2px;
+}
+
+
+
+
+.zk #ganttpanel .z-button-cm {
+ border: 0px;
+}
+
+#ganttpanel {
+ height:400px; /* 800 */
+ width: 900px;
+}
+
+#ganttpanel table {
+ float:left;
+ padding:0;
+ margin:0;
+ overflow:hidden;
+}
+
+#ganttpanel table td {
+ padding:0;
+}
+
+/* -------------- Timetracker -------------- */
+.timetracker_fake_row {
+ height: 80px;
+}
+
+/* Forces every zoom level the same table width */
+#timetracker table {
+ border-collapse: collapse;
+}
+
+#timetracker .second_level_ tr {
+ height:14px;
+}
+
+/* Watermark alternate row color */
+#watermark .timetracker_column_even {
+ background-color: #EEEEEE;
+}
+
+/* Background image for current day vertical line */
+#watermark .timetracker_column_today {
+ background-image: url("/ganttzk-demo-webapp/zkau/web/ganttz/img/watermark_today.png");
+ background-repeat: repeat-y;
+}
+
+#watermark .bankHoliday {
+ background-color: #FFEEEE; !important;
+}
+
+
+/* Reduce spacing and font-size for watermark legend */
+.z-columns, .z-column {
+ font-size: 8px !important;
+ text-align: center;
+ padding:0 0 0 0 !important;
+}
+
+table {
+ margin:0px;
+ padding:0px;
+ border:0px;
+}
+
+#scroll_container {
+ margin-top:70px;
+ height:300px; /* Recalculated based on window */
+ width:500px; /* Recalculated based on window */
+ overflow:hidden;
+ float:left;
+ position:absolute;
+ left:285px; /* Ganntz.ListdetailsWidth + borders = 280 + 5 */
+ /* border:solid green 1px; */
+}
+
+#timetracker {
+ /* border: solid 1px red; */
+ position:absolute;
+ left:285px; /* Ganntz.ListdetailsWidth + borders = 280 + 5 */
+ height:500px; /* zkTasklist.SCROLL_CONTAINER_INITIAL_HEIGHT (dynamic) */
+ width:600px; /* zkTasklist.SCROLL_CONTAINER_INITIAL_WIDTH (dynamic) */
+ position:absolute;
+ overflow-x:hidden;
+ float:left;
+}
+
+#zoom_buttons {
+ position:relative;
+}
+
+tr.z-vbox-sep {
+ height: 0px;
+ padding: 0px;
+ margin: 0px;
+}
+
+
+#ganttpanel_scroller_x, #ganttpanel_scroller_y {
+ position:absolute;
+ float:left;
+ overflow:auto;
+}
+
+#ganttpanel_scroller_x {
+
+ top: 600px; /* (dynamic) */
+ left: 285px; /* Ganntz.ListdetailsWidth + borders = 280 + 5 */
+ width:635px;
+ height:15px;
+}
+
+#ganttpanel_inner_scroller_x {
+ /* must be resized on ganttpanel javascript adjust size */
+ width:9000px; /* Real canvas dimensions */
+ height:15px; /* Scroll constant */
+}
+
+#ganttpanel_scroller_y {
+ top: 160px; /* Fixed top position */
+ left: 920px;
+ width:15px;
+ height:330px;
+}
+#ganttpanel_inner_scroller_y {
+ width:15px; /* Scroll constant */
+ height:1350px; /* Modified when added or removed tasks, or zoom adjustments */
+}
+
+
+.footer {
+ /* Pending to calculate general position */
+ display:none;
+}
+
+/* Hide at the beginning */
+#ganttpanel_scroller_x {
+ display:none;
+}
+#ganttpanel_scroller_y {
+ display:none;
+}
diff --git a/ganttzk-demo-webapp/src/main/webapp/planner/main.zul b/ganttzk-demo-webapp/src/main/webapp/planner/main.zul
new file mode 100644
index 000000000..8ac3f1ddf
--- /dev/null
+++ b/ganttzk-demo-webapp/src/main/webapp/planner/main.zul
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${c:l('task.name')}
+
+
+
+ ${c:l('task.start')}
+
+
+
+ ${c:l('task.end')}
+
+
+
+ ${c:l('task.notes')}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 1345a7bb4..6b48b299c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
navalplanner-business
navalplanner-gantt-zk
navalplanner-webapp
+ ganttzk-demo-webapp