ItEr52S11RFInfraestucturaEscenariosItEr51S11: Created page to manage scenarios.

This commit is contained in:
Manuel Rego Casasnovas 2010-03-24 09:47:12 +01:00
parent b7531f6e2c
commit ac2e07d89f
13 changed files with 752 additions and 1 deletions

View file

@ -41,4 +41,6 @@ public interface IScenarioDAO extends IGenericDAO<Scenario, Long> {
List<Scenario> getAll();
boolean thereIsOtherWithSameName(Scenario scenario);
}

View file

@ -115,4 +115,22 @@ public class ScenarioDAO extends GenericDAOHibernate<Scenario, Long> implements
return list(Scenario.class);
}
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@Override
public boolean thereIsOtherWithSameName(Scenario scenario) {
try {
Scenario withSameName = findByName(scenario.getName());
return areDifferentInDB(withSameName, scenario);
} catch (InstanceNotFoundException e) {
return false;
}
}
private boolean areDifferentInDB(Scenario one, Scenario other) {
if ((one.getId() == null) || (other.getId() == null)) {
return true;
}
return !one.getId().equals(other.getId());
}
}

View file

@ -20,6 +20,8 @@
package org.navalplanner.business.scenarios.entities;
import static org.navalplanner.business.i18n.I18nHelper._;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -32,6 +34,7 @@ import org.navalplanner.business.common.BaseEntity;
import org.navalplanner.business.common.Registry;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.scenarios.bootstrap.PredefinedScenarios;
import org.navalplanner.business.scenarios.daos.IScenarioDAO;
/**
@ -51,6 +54,8 @@ public class Scenario extends BaseEntity {
*/
private Map<Order, OrderVersion> orders = new HashMap<Order, OrderVersion>();
private Scenario predecessor = null;
public static Scenario create(String name) {
return create(new Scenario(name));
}
@ -67,6 +72,11 @@ public class Scenario extends BaseEntity {
this.name = name;
}
public Scenario(String name, Scenario predecessor) {
this.name = name;
this.predecessor = predecessor;
}
public void addOrder(Order order) {
if (!orders.values().contains(order)) {
orders.put(order, OrderVersion.createInitialVersion());
@ -94,6 +104,10 @@ public class Scenario extends BaseEntity {
this.description = description;
}
public Scenario getPredecessor() {
return predecessor;
}
@AssertTrue(message = "name is already used")
public boolean checkConstraintUniqueName() {
if (StringUtils.isBlank(name)) {
@ -115,4 +129,26 @@ public class Scenario extends BaseEntity {
}
}
public boolean isDerived() {
return predecessor != null;
}
public Scenario newDerivedScenario() {
return new Scenario(_("Derived from {0}", name), this);
}
public boolean isPredefined() {
if (name == null) {
return false;
}
for (PredefinedScenarios predefinedScenario : PredefinedScenarios
.values()) {
if (predefinedScenario.getName().equals(name)) {
return true;
}
}
return false;
}
}

View file

@ -21,6 +21,8 @@
<many-to-many column="ORDER_VERSION_ID" class="OrderVersion"/>
</map>
<many-to-one name="predecessor" cascade="all" class="Scenario" />
</class>
<!-- OrderVersion -->

View file

@ -228,7 +228,8 @@ public class CustomMenuController extends Div implements IMenuItemsRegister {
subItem(_("Quality forms"),"/qualityforms/qualityForms.zul","12-formularios-calidad.html#administraci-n-de-formularios-de-calidade"),
subItem(_("Manage user profiles"), "/users/profiles.zul","13-usuarios.html#administraci-n-de-perfiles"),
subItem(_("Manage user accounts"), "/users/users.zul","13-usuarios.html#administraci-n-de-usuarios"),
subItem(_("Manage external companies"), "/externalcompanies/externalcompanies.zul",""));
subItem(_("Manage external companies"), "/externalcompanies/externalcompanies.zul",""),
subItem(_("Manage scenarios"), "/scenarios/scenarios.zul",""));
}
topItem(_("Reports"), "", "",

View file

@ -0,0 +1,59 @@
/*
* This file is part of NavalPlan
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.web.scenarios;
import java.util.List;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.scenarios.entities.Scenario;
/**
* Contract for {@link ScenarioModel}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public interface IScenarioModel {
/*
* Non conversational steps
*/
List<Scenario> getScenarios();
/*
* Initial conversation steps
*/
void initEdit(Scenario scenario);
void initCreateDerived(Scenario scenario);
/*
* Intermediate conversation steps
*/
Scenario getScenario();
/*
* Final conversation steps
*/
void confirmSave() throws ValidationException;
void cancel();
}

View file

@ -0,0 +1,191 @@
/*
* This file is part of NavalPlan
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.web.scenarios;
import static org.navalplanner.web.I18nHelper._;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.navalplanner.web.common.IMessagesForUser;
import org.navalplanner.web.common.Level;
import org.navalplanner.web.common.MessagesForUser;
import org.navalplanner.web.common.OnlyOneVisible;
import org.navalplanner.web.common.Util;
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.util.GenericForwardComposer;
import org.zkoss.zul.Button;
import org.zkoss.zul.Label;
import org.zkoss.zul.SimpleTreeNode;
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.TreeitemRenderer;
import org.zkoss.zul.Treerow;
import org.zkoss.zul.api.Window;
/**
* Controller for CRUD actions over a {@link Scenario}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public class ScenarioCRUDController extends GenericForwardComposer {
private IScenarioModel scenarioModel;
private Window listWindow;
private Window createWindow;
private Window editWindow;
private OnlyOneVisible visibility;
private IMessagesForUser messagesForUser;
private Component messagesContainer;
private ScenariosTreeitemRenderer scenariosTreeitemRenderer = new ScenariosTreeitemRenderer();
public Scenario getScenario() {
return scenarioModel.getScenario();
}
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
messagesForUser = new MessagesForUser(messagesContainer);
comp.setVariable("scenarioController", this, true);
getVisibility().showOnly(listWindow);
}
public void cancel() {
scenarioModel.cancel();
goToList();
}
public void goToList() {
Util.reloadBindings(listWindow);
getVisibility().showOnly(listWindow);
}
public void goToEditForm(Scenario scenario) {
scenarioModel.initEdit(scenario);
getVisibility().showOnly(editWindow);
Util.reloadBindings(editWindow);
}
public void save() {
try {
scenarioModel.confirmSave();
messagesForUser.showMessage(Level.INFO, _("Scenario \"{0}\" saved",
scenarioModel.getScenario().getName()));
goToList();
} catch (ValidationException e) {
messagesForUser.showInvalidValues(e);
}
}
private OnlyOneVisible getVisibility() {
if (visibility == null) {
visibility = new OnlyOneVisible(listWindow, createWindow,
editWindow);
}
return visibility;
}
public void goToCreateDerivedForm(Scenario scenario) {
scenarioModel.initCreateDerived(scenario);
getVisibility().showOnly(createWindow);
Util.reloadBindings(createWindow);
}
public ScenariosTreeModel getScenariosTreeModel() {
return new ScenariosTreeModel(new ScenarioTreeRoot(scenarioModel
.getScenarios()));
}
public ScenariosTreeitemRenderer getScenariosTreeitemRenderer() {
return scenariosTreeitemRenderer;
}
public class ScenariosTreeitemRenderer implements TreeitemRenderer {
@Override
public void render(Treeitem item, Object data) throws Exception {
SimpleTreeNode simpleTreeNode = (SimpleTreeNode) data;
final Scenario scenario = (Scenario) simpleTreeNode.getData();
item.setValue(data);
Treerow treerow = new Treerow();
Treecell nameTreecell = new Treecell();
Label nameLabel = new Label(scenario.getName());
nameTreecell.appendChild(nameLabel);
treerow.appendChild(nameTreecell);
Treecell operationsTreecell = new Treecell();
Button createDerivedButton = new Button();
createDerivedButton.setTooltiptext(_("Create derived"));
createDerivedButton.setSclass("icono");
createDerivedButton.setImage("/common/img/ico_derived1.png");
createDerivedButton.setHoverImage("/common/img/ico_derived.png");
createDerivedButton.addEventListener(Events.ON_CLICK,
new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
goToCreateDerivedForm(scenario);
}
});
operationsTreecell.appendChild(createDerivedButton);
Button editButton = new Button();
editButton.setTooltiptext(_("Edit"));
editButton.setSclass("icono");
editButton.setImage("/common/img/ico_editar1.png");
editButton.setHoverImage("/common/img/ico_editar.png");
editButton.addEventListener(Events.ON_CLICK, new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
goToEditForm(scenario);
}
});
operationsTreecell.appendChild(editButton);
treerow.appendChild(operationsTreecell);
item.appendChild(treerow);
// Show the tree expanded at start
item.setOpen(true);
}
}
}

View file

@ -0,0 +1,130 @@
/*
* This file is part of NavalPlan
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.web.scenarios;
import static org.navalplanner.web.I18nHelper._;
import java.util.List;
import org.apache.commons.lang.Validate;
import org.hibernate.validator.InvalidValue;
import org.navalplanner.business.calendars.entities.BaseCalendar;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.scenarios.daos.IScenarioDAO;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.navalplanner.web.common.concurrentdetection.OnConcurrentModification;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Model for UI operations related to {@link Scenario}.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Qualifier("main")
@OnConcurrentModification(goToPage = "/scenarios/scenarios.zul")
public class ScenarioModel implements IScenarioModel {
/**
* Conversation state
*/
private Scenario scenario;
@Autowired
private IScenarioDAO scenarioDAO;
/*
* Non conversational steps
*/
@Override
@Transactional(readOnly = true)
public List<Scenario> getScenarios() {
return scenarioDAO.getAll();
}
/*
* Initial conversation steps
*/
@Override
@Transactional(readOnly = true)
public void initEdit(Scenario scenario) {
Validate.notNull(scenario);
forceLoad(scenario);
this.scenario = scenario;
}
private void forceLoad(Scenario scenario) {
scenarioDAO.reattach(scenario);
scenario.getOrders().keySet();
}
@Override
@Transactional(readOnly = true)
public void initCreateDerived(Scenario scenario) {
Validate.notNull(scenario);
forceLoad(scenario);
this.scenario = scenario.newDerivedScenario();
}
/*
* Intermediate conversation steps
*/
@Override
public Scenario getScenario() {
return scenario;
}
/*
* Final conversation steps
*/
@Override
@Transactional
public void confirmSave() throws ValidationException {
if (scenarioDAO.thereIsOtherWithSameName(scenario)) {
InvalidValue[] invalidValues = { new InvalidValue(_(
"{0} already exists", scenario.getName()),
BaseCalendar.class, "name", scenario.getName(), scenario) };
throw new ValidationException(invalidValues,
_("Could not save the scenario"));
}
scenarioDAO.save(scenario);
}
@Override
public void cancel() {
resetState();
}
private void resetState() {
scenario = null;
}
}

View file

@ -0,0 +1,69 @@
/*
* This file is part of NavalPlan
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.web.scenarios;
import java.util.ArrayList;
import java.util.List;
import org.navalplanner.business.scenarios.entities.Scenario;
/**
* Class that represents a root node for the {@link Scenario} tree.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public class ScenarioTreeRoot {
private List<Scenario> rootScenarios = new ArrayList<Scenario>();
private List<Scenario> derivedScenarios = new ArrayList<Scenario>();
/**
* Creates a {@link ScenarioTreeRoot} using the list of {@link Scenario}
* passed as argument.
*
* @param scenarios
* All the {@link Scenario} that will be shown in the tree.
*/
public ScenarioTreeRoot(List<Scenario> scenarios) {
for (Scenario scenario : scenarios) {
if (scenario.isDerived()) {
derivedScenarios.add(scenario);
} else {
rootScenarios.add(scenario);
}
}
}
/**
* Returns all the {@link Scenario} that has no parent.
*/
public List<Scenario> getRootScenarios() {
return rootScenarios;
}
/**
* Returns all the {@link Scenario} that has a parent.
*/
public List<Scenario> getDerivedScenarios() {
return derivedScenarios;
}
}

View file

@ -0,0 +1,111 @@
/*
* This file is part of NavalPlan
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.navalplanner.web.scenarios;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.zkoss.zul.SimpleTreeModel;
import org.zkoss.zul.SimpleTreeNode;
/**
* Model for the {@link Scenario} tree.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public class ScenariosTreeModel extends SimpleTreeModel {
private static Map<Scenario, List<Scenario>> relationParentChildren = new HashMap<Scenario, List<Scenario>>();
public ScenariosTreeModel(ScenarioTreeRoot root) {
super(createRootNodeAndDescendants(root, root.getRootScenarios(), root
.getDerivedScenarios()));
}
private static SimpleTreeNode createRootNodeAndDescendants(
ScenarioTreeRoot root, List<Scenario> rootScenarios,
List<Scenario> derivedScenarios) {
fillHashParentChildren(rootScenarios, derivedScenarios);
return new SimpleTreeNode(root, asNodes(rootScenarios));
}
private static List<SimpleTreeNode> asNodes(List<Scenario> scenarios) {
if (scenarios == null) {
return new ArrayList<SimpleTreeNode>();
}
ArrayList<SimpleTreeNode> result = new ArrayList<SimpleTreeNode>();
for (Scenario scenario : scenarios) {
result.add(asNode(scenario));
}
return result;
}
private static SimpleTreeNode asNode(Scenario scenario) {
List<Scenario> children = relationParentChildren.get(scenario);
return new SimpleTreeNode(scenario, asNodes(children));
}
private static void fillHashParentChildren(
List<Scenario> rootScenarios,
List<Scenario> derivedScenarios) {
for (Scenario root : rootScenarios) {
relationParentChildren.put(root, new ArrayList<Scenario>());
}
for (Scenario derived : derivedScenarios) {
Scenario parent = derived.getPredecessor();
List<Scenario> siblings = relationParentChildren.get(parent);
if (siblings == null) {
siblings = new ArrayList<Scenario>();
siblings.add(derived);
relationParentChildren.put(parent, siblings);
} else {
siblings.add(derived);
}
}
}
@Override
public boolean isLeaf(Object node) {
if (node == null) {
return true;
}
SimpleTreeNode simpleTreeNode = (SimpleTreeNode) node;
Scenario scenario = (Scenario) simpleTreeNode.getData();
List<Scenario> children = relationParentChildren.get(scenario);
if (children == null) {
return true;
}
return children.isEmpty();
}
}

View file

@ -0,0 +1,57 @@
<!--
This file is part of NavalPlan
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 <http://www.gnu.org/licenses/>.
-->
<window id="${arg.top_id}" title="${arg.title}">
<grid fixedLayout="true" style="margin-bottom: 10px;">
<columns>
<column width="200px" />
<column/>
</columns>
<rows>
<row>
<label value="${i18n:_('Name')}" />
<textbox value="@{scenarioController.scenario.name}" width="300px"
constraint="no empty:${i18n:_('cannot be null or empty')}"
disabled="@{scenarioController.scenario.predefined}" />
</row>
<row visible="@{scenarioController.scenario.derived}">
<label value="${i18n:_('Predecessor')}" />
<label value="@{scenarioController.scenario.predecessor.name}" />
</row>
<row>
<label value="${i18n:_('Description')}" />
<textbox value="@{scenarioController.scenario.description}"
rows="5" width="300px" />
</row>
</rows>
</grid>
<hbox>
<button onClick="scenarioController.save();"
label="${arg.save_button_label}"
sclass="save-button global-action"
visible="${arg.save_button_visible}" />
<button onClick="scenarioController.cancel();"
label="${arg.cancel_button_label}"
sclass="cancel-button global-action" />
</hbox>
</window>

View file

@ -0,0 +1,30 @@
<!--
This file is part of NavalPlan
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 <http://www.gnu.org/licenses/>.
-->
<window id="${arg.top_id}" title="${i18n:_('Scenarios List')}">
<tree id="tree" model="@{scenarioController.scenariosTreeModel}"
treeitemRenderer="@{scenarioController.scenariosTreeitemRenderer}"
zclass="z-dottree">
<treecols sizable="true">
<treecol label="${i18n:_('Name')}" />
<treecol label="${i18n:_('Operations')}" />
</treecols>
</tree>
</window>

View file

@ -0,0 +1,45 @@
<!--
This file is part of NavalPlan
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 <http://www.gnu.org/licenses/>.
-->
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?page id="work_report_admin"?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/common/layout/template.zul"?>
<?link rel="stylesheet" type="text/css" href="/common/css/navalplan.css"?>
<?link rel="stylesheet" type="text/css" href="/common/css/navalplan_zk.css"?>
<?link rel="stylesheet" type="text/css" href="/resources/css/resources.css"?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<?component name="list" inline="true" macroURI="_list.zul"?>
<?component name="edition" inline="true" macroURI="_edition.zul"?>
<zk>
<window self="@{define(content)}"
apply="org.navalplanner.web.scenarios.ScenarioCRUDController">
<vbox id="messagesContainer"></vbox>
<list top_id="listWindow" />
<edition top_id="createWindow" title="${i18n:_('Create Scenario')}"
save_button_label="${i18n:_('Save')}"
save_button_visible="true"
cancel_button_label="${i18n:_('Cancel')}" />
<edition top_id="editWindow" title="${i18n:_('Edit Scenario')}"
save_button_label="${i18n:_('Save')}"
save_button_visible="true"
cancel_button_label="${i18n:_('Cancel')}" />
</window>
</zk>