From ea2bc254ed01b60134392697b19d285beb673bca Mon Sep 17 00:00:00 2001 From: Machac Date: Mon, 27 Jan 2025 12:06:33 +0100 Subject: [PATCH 001/174] Release/6.5.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c90aec54bc..332d1595bfb 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.netgrif application-engine - 6.4.1-SNAPSHOT + 6.5.0-SNAPSHOT jar NETGRIF Application Engine From d02feea813b01ca2802495abd65728208e35842d Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 28 Jan 2025 10:08:55 +0100 Subject: [PATCH 002/174] =?UTF-8?q?[NAE-2034]=20Otvorenie=20prv=C3=A9ho=20?= =?UTF-8?q?viewu=20-=20implement=20auto-select=20for=20menu=20items?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflow/domain/menu/MenuItemBody.java | 3 + .../domain/menu/MenuItemConstants.java | 2 + .../engine-processes/preference_item.xml | 66 ++++++++++++++++++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java index 0faac7601fb..cdb09cdbf29 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java @@ -35,6 +35,7 @@ public class MenuItemBody { private boolean useTabIcon = true; private boolean useCustomView = false; private String customViewSelector; + private boolean isAutoSelect = false; // case view attributes private String caseViewSearchType = "fulltext_advanced"; @@ -179,6 +180,7 @@ private Map> toDataSet(String parentId, String nodeP ArrayList filterIdCaseRefValue = new ArrayList<>(); if (this.filter != null) { filterIdCaseRefValue.add(this.filter.getStringId()); + putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CONTAINS_FILTER, FieldType.BOOLEAN, true); } ArrayList parentIdCaseRef = new ArrayList<>(); if (parentId != null) { @@ -204,6 +206,7 @@ private Map> toDataSet(String parentId, String nodeP this.useCustomView); putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR, FieldType.TEXT, this.customViewSelector); + putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_IS_AUTO_SELECT, FieldType.BOOLEAN, this.isAutoSelect); // CASE putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java index 2b4f5a0fec9..8d9ab8a9190 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java @@ -10,6 +10,7 @@ public enum MenuItemConstants { // FIELDS PREFERENCE_ITEM_FIELD_NEW_FILTER_ID("new_filter_id"), PREFERENCE_ITEM_FIELD_FILTER_CASE("filter_case"), + PREFERENCE_ITEM_FIELD_CONTAINS_FILTER("contains_filter"), PREFERENCE_ITEM_FIELD_PARENT_ID("parentId"), PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS("childItemIds"), PREFERENCE_ITEM_FIELD_HAS_CHILDREN("hasChildren"), @@ -33,6 +34,7 @@ public enum MenuItemConstants { PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW("use_custom_view"), PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR("custom_view_selector"), PREFERENCE_ITEM_FIELD_CASE_VIEW_SEARCH_TYPE("case_view_search_type"), + PREFERENCE_ITEM_FIELD_IS_AUTO_SELECT("is_auto_select"), PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_TITLE("create_case_button_title"), PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_ICON("create_case_button_icon"), PREFERENCE_ITEM_FIELD_BANNED_NETS_IN_CREATION("case_banned_nets_in_creation"), diff --git a/src/main/resources/petriNets/engine-processes/preference_item.xml b/src/main/resources/petriNets/engine-processes/preference_item.xml index 81deeec6d7f..6f2d35a2deb 100644 --- a/src/main/resources/petriNets/engine-processes/preference_item.xml +++ b/src/main/resources/petriNets/engine-processes/preference_item.xml @@ -125,6 +125,24 @@ change toBeUpdated options { newOptions } } + + { + -> + String query = String.format("processIdentifier:preference_item AND uriNodeId:%s AND dataSet.is_auto_select.booleanValue:true AND NOT stringId:%s", + useCase.uriNodeId, useCase.stringId) + def itemCase = findCaseElastic(query) + if (itemCase == null) { + return + } + + setData("item_settings", itemCase, [ + "is_auto_select": [ + "value": false, + "type": "boolean" + ] + ]) + } + parentId @@ -470,6 +488,11 @@ new_filter_id </data> + <data type="boolean" immediate="true"> + <id>contains_filter</id> + <title/> + <init>false</init> + </data> <data type="enumeration_map"> <id>filter_autocomplete_selection</id> <title name="filter_autocomplete_selection">Select new filter @@ -511,9 +534,12 @@ trans: t.item_settings, update_filter: f.update_filter, + contains_filter: f.contains_filter, filter_case: f.filter_case, filterAutocomplete: f.filter_autocomplete_selection; + + change contains_filter value { filterAutocomplete.value != null && filterAutocomplete.value != "" } change filter_case value { [filterAutocomplete.value] } change filterAutocomplete value { "" } make update_filter,visible on trans when { true } @@ -586,6 +612,20 @@ Custom view configuration selector Example: "demo-tabbed-views" + + is_auto_select + View auto selection + If selected, the view will be automatically opened + false + + is_auto_select: f.is_auto_select; + + if (!is_auto_select.value) + return + + removeAnyAutoSelectFlagInFolder() + + @@ -1098,6 +1138,8 @@ Uveďte názov uzlu, ktorý chcete pridať Zoznam uzlov reprezentujúce cieľovú URI Pridať + Automatické zvolenie zobrazenia + Po automatickom zvolení sa dané zobrazenie používateľovi otvorí Ikonevorschau @@ -1176,6 +1218,8 @@ Rollen, für die wird den Menüeintrag ausgeblendet Nächste URI-Teil angeben Teile der Ziel URI + Automatische Anzeigeauswahl + Wenn ausgewählt, wird die Ansicht automatisch geöffnet @@ -1388,7 +1432,7 @@ - use_custom_view + is_auto_select editable @@ -1398,6 +1442,20 @@ 1 1 + standard + + + + use_custom_view + + editable + + + 1 + 2 + 1 + 1 + outline @@ -1464,10 +1522,10 @@ visible - 1 + 2 2 1 - 3 + 2 outline @@ -1915,10 +1973,12 @@ new_filter_id: f.new_filter_id, + contains_filter: f.contains_filter, filterTaskRef: f.current_filter_preview, filterCaseRef: f.filter_case; change filterCaseRef value { [new_filter_id.value] } + change contains_filter value { new_filter_id.value != null && new_filter_id.value != "" } def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) change filterTaskRef value {return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId]} From 458b89e18a107655ae613477e2d499f7b6037ce7 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Wed, 5 Feb 2025 09:48:21 +0100 Subject: [PATCH 003/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - create processes for managing dashboards - dashboard_management and dashboard_item - create runner for importing dashboard management processes --- .../startup/DashboardManagementRunner.groovy | 61 ++ .../engine/startup/RunnerController.groovy | 1 + .../engine-processes/dashboard_item.xml | 588 ++++++++++++++++++ .../engine-processes/dashboard_management.xml | 497 +++++++++++++++ 4 files changed, 1147 insertions(+) create mode 100644 src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy create mode 100644 src/main/resources/petriNets/engine-processes/dashboard_item.xml create mode 100644 src/main/resources/petriNets/engine-processes/dashboard_management.xml diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy new file mode 100644 index 00000000000..7d9a2f74c78 --- /dev/null +++ b/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy @@ -0,0 +1,61 @@ +package com.netgrif.application.engine.startup + +import com.netgrif.application.engine.petrinet.domain.PetriNet +import com.netgrif.application.engine.petrinet.domain.VersionType +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.stereotype.Component + +@Slf4j +@Component +@ConditionalOnProperty(value = "nae.dashboard-management.enabled", matchIfMissing = true) +class DashboardManagementRunner extends AbstractOrderedCommandLineRunner { + + @Autowired + private IPetriNetService petriNetService + + @Autowired + private ImportHelper helper + + @Autowired + private SystemUserRunner systemCreator + + public static final String DASHBOARD_MANAGEMENT_NET_IDENTIFIER = "dashboard_management" + private static final String DASHBOARD_MANAGEMENT_FILE_NAME = "engine-processes/dashboard_management.xml" + + public static final String DASHBOARD_ITEM_NET_IDENTIFIER = "dashboard_item" + private static final String DASHBOARD_ITEM_FILE_NAME = "engine-processes/dashboard_item.xml" + + @Override + void run(String... args) throws Exception { + createDashboardManagementNet() + createDashboardItemNet() + } + + Optional createDashboardManagementNet() { + importProcess("Petri net for filters", DASHBOARD_MANAGEMENT_NET_IDENTIFIER, DASHBOARD_MANAGEMENT_FILE_NAME) + } + + Optional createDashboardItemNet() { + importProcess("Petri net for filter preferences", DASHBOARD_ITEM_NET_IDENTIFIER, DASHBOARD_ITEM_FILE_NAME) + } + + + Optional importProcess(String message, String netIdentifier, String netFileName) { + PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) + if (filter != null) { + log.info("${message} has already been imported.") + return Optional.of(filter) + } + + Optional filterNet = helper.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) + + if (!filterNet.isPresent()) { + log.error("Import of ${message} failed!") + } + + return filterNet + } +} diff --git a/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy b/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy index f02d30e1c8f..f58f29e1eea 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy @@ -21,6 +21,7 @@ class RunnerController { DefaultFiltersRunner, ImpersonationRunner, DashboardRunner, + DashboardManagementRunner, SuperCreator, FlushSessionsRunner, MailRunner, diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml new file mode 100644 index 00000000000..dcee9ca500c --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -0,0 +1,588 @@ + + dashboard_item + DBI + Dashboard item + crop_square + true + false + false + + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + + system + System + + + admin + Admin + + + + item_id + Dashboard item ID + Unique identifier of dashboard item + + item_id_set + + + item_id: f.item_id; + + if (item_id.value == "") { + return + } + def itemWithIdExists = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\"" as String, org.springframework.data.domain.PageRequest.of(0, 1)).isEmpty() + if (!itemWithIdExists) { + change item_id value { "" } + throw new IllegalArgumentException("Dashboard item with given ID already exists.") + } + + + + + + preference_items_list + Existing menu items + List of all existing menu items + + autocomplete_dynamic + + + preference_items_list_get + + + preference_items_list: f.preference_items_list; + + def preferenceItemCases = findCasesElastic("processIdentifier:\"preference_item\"" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + .collectEntries { [(it.stringId): it.dataSet["menu_name"].value] } + change preference_items_list options { preferenceItemCases } + + + + + preference_items_list_set + + + inherit_name: f.inherit_name, + inherit_icon: f.inherit_icon, + preference_item_taskRef: f.preference_item_taskRef, + filter_divider: f.filter_divider, + preference_items_list: f.preference_items_list, + trans: t.configuration; + + change inherit_icon value { true } + change inherit_name value { true } + if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { + make [inherit_name, inherit_icon], editable on trans when { true } + make [preference_item_taskRef, filter_divider], visible on trans when { true } + def preferenceItemCase = workflowService.findOne(preference_items_list.value) + change preference_item_taskRef value { [preferenceItemCase.tasks.find { it.transition == "item_settings" }?.task] } + } else { + make [inherit_name, inherit_icon], visible on trans when { true } + make [preference_item_taskRef, filter_divider], hidden on trans when { true } + change preference_item_taskRef value { [] } + } + + + + + + is_internal + Internal item + Internal menu item or link to external website URL + true + + is_internal_set + + + item_name: f.item_name, + item_icon: f.item_icon, + preference_items_list: f.preference_items_list, + external_url: f.external_url, + is_internal: f.is_internal; + + if (is_internal.value) { + change preference_items_list value { "" } + } else { + change item_icon value { "" } + change item_name value { new com.netgrif.application.engine.petrinet.domain.I18nString("") } + change external_url value { "" } + } + + + is_internal: f.is_internal, + item_name: f.item_name, + item_icon: f.item_icon, + preference_items_list: f.preference_items_list, + item_icon_preview: f.item_icon_preview, + external_url: f.external_url, + inherit_name: f.inherit_name, + inherit_icon: f.inherit_icon, + preference_item_taskRef: f.preference_item_taskRef, + filter_divider: f.filter_divider, + trans: t.configuration; + + if (is_internal.value) { + make [preference_items_list], editable on trans when { true } + make [external_url], hidden on trans when { true } + } else { + make [item_icon, item_name, external_url], editable on trans when { true } + make item_icon_preview, visible on trans when { true } + make [inherit_name, inherit_icon, preference_items_list, preference_item_taskRef, filter_divider], hidden on trans when { true } + } + + + + + + external_url + External URL + Link for external website URL + + + item_icon + Item icon identifier + Material icon identifier. List of icons with identifiers is available online. Icon will be displayed in dashboard. + + icon: f.this, + iconPreview: f.item_icon_preview; + + changeCaseProperty "icon" about { icon.value } + + if (icon.value == "") { + change iconPreview value {"""]]>} + return; + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + item_icon_preview + + <component> + <name>htmltextarea</name> + </component> + </data> + <data type="i18n" immediate="true"> + <id>item_name</id> + <title name="item_name">Item name + Item name which will be displayed in dashboard + + + inherit_icon + Inherit icon + Check if icon should be inherited from menu item + true + + inherit_icon_set + + + inherit_icon: f.inherit_icon, + item_icon_preview: f.item_icon_preview, + item_icon: f.item_icon, + preference_items_list: f.preference_items_list, + trans: t.configuration; + + if (inherit_icon.value) { + make [item_icon, item_icon_preview], visible on trans when { true } + if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { + def preferenceItemCase = workflowService.findOne(preference_items_list.value) + change item_icon value { preferenceItemCase.dataSet["menu_icon"].value } + } else { + change item_icon value { "" } + } + } else { + make item_icon, editable on trans when { true } + make item_icon_preview, visible on trans when { true } + change item_icon value { "" } + } + + + + + + inherit_name + Inherit name from menu item + Name should be inherited from menu item + true + + inherit_name_set + + + inherit_name: f.inherit_name, + item_name: f.item_name, + preference_items_list: f.preference_items_list; + + if (inherit_name.value) { + if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { + def preferenceItemCase = workflowService.findOne(preference_items_list.value) + change item_name value { preferenceItemCase.dataSet["menu_name"].value } + } else { + change item_name value { new com.netgrif.application.engine.petrinet.domain.I18nString("") } + } + } else { + change item_name value { new com.netgrif.application.engine.petrinet.domain.I18nString("") } + } + + + inherit_name: f.inherit_name, + item_name: f.item_name, + trans: t.configuration; + + if (inherit_name.value) { + make item_name, visible on trans when { true } + } else { + make item_name, editable on trans when { true } + } + + + + + + preference_item_taskRef + + </data> + <data type="i18n"> + <id>filter_divider</id> + <title/> + <init dynamic="true"> + new com.netgrif.application.engine.petrinet.domain.I18nString("Menu item data", [sk: "Dáta menu položky", de: "Daten zum Menüpunkt"]) + </init> + <component> + <name>divider</name> + <property key="fontSize">16</property> + </component> + </data> + <data type="boolean" immediate="true"> + <id>is_active</id> + <title/> + <init>false</init> + </data> + + + <!-- I18NS --> + <i18n locale="sk"> + <i18nString name="item_id">ID položky panelu</i18nString> + <i18nString name="item_id_desc">Unikátny identifikátor položky panelu</i18nString> + <i18nString name="preference_items_list">Existujúce menu položky</i18nString> + <i18nString name="preference_items_list_desc">Zoznam všetkých existujúcich menu položiek</i18nString> + <i18nString name="is_internal">Interná položka</i18nString> + <i18nString name="is_internal_desc">Položka interného menu alebo odkaz na externú webovú adresu URL</i18nString> + <i18nString name="external_url">Externá adresa URL</i18nString> + <i18nString name="external_url_desc">Odkaz na externú webovú adresu URL</i18nString> + <i18nString name="item_icon">Identifikátor ikony položky</i18nString> + <i18nString name="item_icon_desc">Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. Ikona sa zobrazí v panely.</i18nString> + <i18nString name="item_name">Názov položky</i18nString> + <i18nString name="item_name_desc">Názov položky, ktorá sa zobrazí v panely</i18nString> + <i18nString name="inherit_icon">Prebrať ikonu</i18nString> + <i18nString name="inherit_icon_desc">Ikona by mala byť prebratá z menu položky</i18nString> + <i18nString name="inherit_name">Prebrať názov</i18nString> + <i18nString name="inherit_name_desc">Názov by mal byť prebratý z menu položky</i18nString> + <i18nString name="configuration_label">Konfigurácia položky panelu</i18nString> + </i18n> + <i18n locale="de"> + <i18nString name="item_id">Dashboard-Element-ID</i18nString> + <i18nString name="item_id_desc">Eindeutiger Bezeichner des Dashboard-Elements</i18nString> + <i18nString name="preference_items_list">Vorhandene Menüpunkte</i18nString> + <i18nString name="preference_items_list_desc">Liste aller vorhandenen Menüpunkte</i18nString> + <i18nString name="is_internal">Interner Posten</i18nString> + <i18nString name="is_internal_desc">Interner Menüpunkt oder Link zu externer Website-URL</i18nString> + <i18nString name="external_url">Externe URL</i18nString> + <i18nString name="external_url_desc">Link für externe Website-URL</i18nString> + <i18nString name="item_icon">Bezeichner des Artikelsymbols</i18nString> + <i18nString name="item_icon_desc">Kennung der Materialikone. Die Liste der Symbole mit den Bezeichnungen ist online verfügbar. Das Symbol wird im Dashboard angezeigt.</i18nString> + <i18nString name="item_name">Name des Gegenstands</i18nString> + <i18nString name="item_name_desc">Artikelbezeichnung, die im Dashboard angezeigt wird</i18nString> + <i18nString name="inherit_icon">Symbol vererben</i18nString> + <i18nString name="inherit_icon_desc">Prüfen, ob das Symbol vom Menüpunkt geerbt werden soll</i18nString> + <i18nString name="inherit_name">Name von Menüpunkt vererben</i18nString> + <i18nString name="inherit_name_desc">Name sollte vom Menüpunkt geerbt werden</i18nString> + <i18nString name="configuration_label">Konfiguration von Dashboard-Elementen</i18nString> + </i18n> + + <!-- TRANSITIONS --> + <transition> + <id>configuration</id> + <x>400</x> + <y>112</y> + <label name="configuration_label">Dashboard item configuration</label> + <icon>tune</icon> + <assignPolicy>auto</assignPolicy> +<!-- <roleRef>--> +<!-- <id>admin</id>--> +<!-- <logic>--> +<!-- <perform>true</perform>--> +<!-- <view>true</view>--> +<!-- <cancel>true</cancel>--> +<!-- <assign>true</assign>--> +<!-- </logic>--> +<!-- </roleRef>--> + <dataGroup> + <id>configuration_d1</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>item_id</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>is_internal</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <dataGroup> + <id>configuration_internal</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>preference_items_list</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>inherit_name</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>inherit_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>3</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <dataGroup> + <id>configuration_general</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>item_name</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>item_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>item_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>3</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + <dataRef> + <id>filter_divider</id> + <logic> + <behavior>hidden</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>preference_item_taskRef</id> + <logic> + <behavior>hidden</behavior> + </logic> + <layout> + <x>0</x> + <y>2</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <dataGroup> + <id>configuration_external</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>external_url</id> + <logic> + <behavior>hidden</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <event type="assign"> + <id>configuration_assign</id> + <actions phase="post"> + <action> + is_active: f.is_active; + + change is_active value { false } + </action> + </actions> + </event> + <event type="finish"> + <id>configuration_finish</id> + <actions phase="pre"> + <action> + item_id: f.item_id, + item_name: f.item_name, + is_internal: f.is_internal, + preference_items_list: f.preference_items_list, + external_url: f.external_url, + is_active: f.is_active; + + if (item_id.value == null || item_id.value == "") { + throw new IllegalArgumentException("Dashboard item ID cannot be empty.") + } + if (is_internal.value) { + if (preference_items_list.value == null || !org.bson.types.ObjectId.isValid(preference_items_list.value)){ + throw new IllegalArgumentException("Internal menu item must be selected.") + } + } else { + if (external_url.value == null || external_url.value == "") { + throw new IllegalArgumentException("External URL cannot be empty.") + } + } + change is_active value { true } + </action> + </actions> + </event> + </transition> + <place> + <id>p1</id> + <x>208</x> + <y>112</y> + <label/> + <tokens>1</tokens> + <static>false</static> + </place> + <arc> + <id>a1</id> + <type>regular</type> + <sourceId>p1</sourceId> + <destinationId>configuration</destinationId> + <multiplicity>1</multiplicity> + <breakpoint> + <x>304</x> + <y>144</y> + </breakpoint> + </arc> + <arc> + <id>a2</id> + <type>regular</type> + <sourceId>configuration</sourceId> + <destinationId>p1</destinationId> + <multiplicity>1</multiplicity> + <breakpoint> + <x>304</x> + <y>80</y> + </breakpoint> + </arc> +</document> diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml new file mode 100644 index 00000000000..c0e5baed459 --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -0,0 +1,497 @@ +<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://petriflow.com/petriflow.schema.xsd"> + <id>dashboard_management</id> + <initials>DBM</initials> + <title>Dashboard management + dashboard + true + false + false + + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + + system + System + + + admin + Admin + + + + dashboard_id + Dashboard ID + Unique identifier of dashboard + + dashboard_id_set + + + dashboard_id: f.dashboard_id; + + if (dashboard_id.value == "") { + return + } + def dashboardWithIdExists = findCasesElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\"" as String, org.springframework.data.domain.PageRequest.of(0, 1)).isEmpty() + if (!dashboardWithIdExists) { + change dashboard_id value { "" } + throw new IllegalArgumentException("Dashboard with given ID already exists.") + } + + + + + + dashboard_name + Dashboard name + Dashboard name to be displayed in application dashboard + + + dashboard_item_list + Dashboard items + List of dashboard items to be displayed in application dashboard + + list + + + + order_down + + <placeholder>south</placeholder> + <component> + <name>icon</name> + <property key="stretch">true</property> + </component> + <event type="set"> + <id>order_down_set</id> + <actions phase="post"> + <action> + dashboard_item_list: f.dashboard_item_list; + + if (dashboard_item_list.value == null || dashboard_item_list.value == "") { + return + } + def keys = dashboard_item_list.options.keySet() as List + def currentIndex = keys.indexOf(dashboard_item_list.value) + if (currentIndex == keys.size() - 1) { + return + } + java.util.Collections.swap(keys, currentIndex, currentIndex + 1) + def newOrder = [:] + keys.forEach { newOrder[it] = dashboard_item_list.options[it]} + change dashboard_item_list options { newOrder } + </action> + </actions> + </event> + </data> + <data type="button"> + <id>order_up</id> + <title/> + <placeholder>north</placeholder> + <component> + <name>icon</name> + <property key="stretch">true</property> + </component> + <event type="set"> + <id>order_up_set</id> + <actions phase="post"> + <action> + dashboard_item_list: f.dashboard_item_list; + + if (dashboard_item_list.value == null || dashboard_item_list.value == "") { + return + } + def keys = dashboard_item_list.options.keySet() as List + def currentIndex = keys.indexOf(dashboard_item_list.value) + if (currentIndex == 0) { + return + } + java.util.Collections.swap(keys, currentIndex, currentIndex - 1) + def newOrder = [:] + keys.forEach { newOrder[it] = dashboard_item_list.options[it]} + change dashboard_item_list options { newOrder } + </action> + </actions> + </event> + </data> + <data type="enumeration_map"> + <id>existing_menu_items</id> + <title name="existing_menu_items">Existing dashboard items + List of all existing dashboard items which can be added to dashboard + + autocomplete_dynamic + + + existing_menu_items_get + + + dashboard_item_list: f.dashboard_item_list, + existing_menu_items: f.existing_menu_items; + + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } + dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) + change existing_menu_items options { dashboardItemCases } + + + + + existing_menu_items_set + + + item_divider: f.item_divider, + dashboard_item_taskRef: f.dashboard_item_taskRef, + existing_menu_items: f.existing_menu_items, + trans: t.configuration; + + if (existing_menu_items.value == null || !org.bson.types.ObjectId.isValid(existing_menu_items.value)) { + change dashboard_item_taskRef value { [] } + make item_divider, hidden on trans when { true } + return + } + def dashboardItemCase = workflowService.findOne(existing_menu_items.value) + make item_divider, visible on trans when { true } + change dashboard_item_taskRef value { [dashboardItemCase.tasks.find { it.transition == "configuration" }?.task] } + + + + + + add_new_item + + <placeholder>Add new item</placeholder> + <event type="set"> + <id>add_new_item_set</id> + <actions phase="post"> + <action> + dashboard_item_list: f.dashboard_item_list, + existing_menu_items: f.existing_menu_items; + + if (existing_menu_items.value == null || !org.bson.types.ObjectId.isValid(existing_menu_items.value)) { + throw new IllegalArgumentException("No menu item was selected.") + } + def dashboardItemOptions = dashboard_item_list.options + dashboardItemOptions.put(existing_menu_items.value, existing_menu_items.options.get(existing_menu_items.value)) + change dashboard_item_list options { dashboardItemOptions } + + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } + dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) + change existing_menu_items options { dashboardItemCases } + change existing_menu_items value { "" } + </action> + </actions> + </event> + </data> + <data type="button"> + <id>remove_new_item</id> + <title/> + <placeholder>Remove item</placeholder> + <event type="set"> + <id>remove_new_item_set</id> + <actions phase="post"> + <action> + dashboard_item_list: f.dashboard_item_list, + existing_menu_items: f.existing_menu_items; + + if (dashboard_item_list.value == null || !org.bson.types.ObjectId.isValid(dashboard_item_list.value)) { + throw new IllegalArgumentException("No menu item was selected.") + } + def dashboardItemOptions = dashboard_item_list.options + dashboardItemOptions.keySet().removeAll([dashboard_item_list.value]) + change dashboard_item_list options { dashboardItemOptions } + + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } + dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) + change existing_menu_items options { dashboardItemCases } + change existing_menu_items value { "" } + </action> + </actions> + </event> + </data> + <data type="taskRef"> + <id>dashboard_item_taskRef</id> + <title/> + </data> + <data type="boolean"> + <id>is_active</id> + <title/> + </data> + <data type="i18n"> + <id>item_divider</id> + <title/> + <init dynamic="true"> + new com.netgrif.application.engine.petrinet.domain.I18nString("Dashboard item data", [sk: "Dáta položky panelu", de: "Daten zum Dashboard-Element"]) + </init> + <component> + <name>divider</name> + <property key="fontSize">18</property> + </component> + </data> + + <!-- I18NS --> + <i18n locale="sk"> + <i18nString name="dashboard_id">ID panelu</i18nString> + <i18nString name="dashboard_id_desc">Unikátny identifikátor panelu</i18nString> + <i18nString name="dashboard_name">Názov panelu</i18nString> + <i18nString name="dashboard_name_desc">Názov panelu, ktorý bude zobrazený v panely aplikácie</i18nString> + <i18nString name="dashboard_item_list">Položky panelu</i18nString> + <i18nString name="dashboard_item_list_desc">Zoznam položiek panalu, ktoré budú zobrazené v panely aplikácie</i18nString> + <i18nString name="existing_menu_items">Existujúce položky panelu</i18nString> + <i18nString name="existing_menu_items_desc">Zoznam všetkých existujúcich položiek panelu, ktoré budú zobrazené v panely aplikácie</i18nString> + <i18nString name="configuration_label">Konfigurácia panela</i18nString> + </i18n> + <i18n locale="de"> + <i18nString name="dashboard_id">Dashboard-ID</i18nString> + <i18nString name="dashboard_id_desc">Eindeutiger Bezeichner des Dashboards</i18nString> + <i18nString name="dashboard_name">Name des Dashboards</i18nString> + <i18nString name="dashboard_name_desc">Name des Dashboards, der im Dashboard der Anwendung angezeigt werden soll</i18nString> + <i18nString name="dashboard_item_list">Dashboard-Elemente</i18nString> + <i18nString name="dashboard_item_list_desc">Liste der Dashboard-Elemente, die im Dashboard der Anwendung angezeigt werden sollen</i18nString> + <i18nString name="existing_menu_items">Vorhandene Dashboard-Elemente</i18nString> + <i18nString name="existing_menu_items_desc">Liste aller vorhandenen Dashboard-Elemente, die dem Dashboard hinzugefügt werden können</i18nString> + <i18nString name="configuration_label">Konfiguration des Dashboards</i18nString> + </i18n> + + <!-- TRANSITIONS --> + <transition> + <id>configuration</id> + <x>400</x> + <y>112</y> + <label name="configuration_label">Dashboard configuration</label> + <icon>settings</icon> + <assignPolicy>auto</assignPolicy> +<!-- <roleRef>--> +<!-- <id>admin</id>--> +<!-- <logic>--> +<!-- <perform>true</perform>--> +<!-- <view>true</view>--> +<!-- <cancel>true</cancel>--> +<!-- <assign>true</assign>--> +<!-- </logic>--> +<!-- </roleRef>--> + <dataGroup> + <id>configuration_d1</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>dashboard_id</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>dashboard_name</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>dashboard_item_list</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>2</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>order_up</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>1</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>order_down</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>2</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>remove_new_item</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>3</x> + <y>1</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>existing_menu_items</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>3</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>add_new_item</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>3</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>item_divider</id> + <logic> + <behavior>hidden</behavior> + </logic> + <layout> + <x>0</x> + <y>4</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>dashboard_item_taskRef</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>5</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <event type="assign"> + <id>configuration_assign</id> + <actions phase="post"> + <action> + is_active: f.is_active; + + change is_active value { false } + </action> + </actions> + </event> + <event type="finish"> + <id>configuration_finish</id> + <actions phase="pre"> + <action> + dashboard_id: f.dashboard_id, + dashboard_name: f.dashboard_name, + is_active: f.is_active; + + if (dashboard_id.value == null || dashboard_id.value == "") { + throw new IllegalArgumentException("Dashboard ID cannot be empty.") + } + change is_active value { true } + </action> + </actions> + </event> + </transition> + <place> + <id>p1</id> + <x>208</x> + <y>112</y> + <label/> + <tokens>1</tokens> + <static>false</static> + </place> + <arc> + <id>a1</id> + <type>regular</type> + <sourceId>p1</sourceId> + <destinationId>configuration</destinationId> + <multiplicity>1</multiplicity> + <breakpoint> + <x>304</x> + <y>144</y> + </breakpoint> + </arc> + <arc> + <id>a2</id> + <type>regular</type> + <sourceId>configuration</sourceId> + <destinationId>p1</destinationId> + <multiplicity>1</multiplicity> + <breakpoint> + <x>304</x> + <y>80</y> + </breakpoint> + </arc> +</document> From 8bf002e1a54de2254b1d899a0d7262e0c45af88d Mon Sep 17 00:00:00 2001 From: Jozef Daxner <daxner.dodo@gmail.com> Date: Fri, 7 Feb 2025 15:07:21 +0100 Subject: [PATCH 004/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - added option to load icon from web (image from web) - added option to change dashboard toolbar logo - added option to chane appearance of dashboard toolbar --- .../engine-processes/dashboard_item.xml | 63 ++++-- .../engine-processes/dashboard_management.xml | 203 +++++++++++++++--- 2 files changed, 226 insertions(+), 40 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index dcee9ca500c..a284be6ebcc 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -26,8 +26,8 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> + <create>true</create> + <delete>true</delete> <view>true</view> </caseLogic> </roleRef> @@ -120,10 +120,12 @@ <action> item_name: f.item_name, item_icon: f.item_icon, + external_icon: f.external_icon, preference_items_list: f.preference_items_list, external_url: f.external_url, is_internal: f.is_internal; + change external_icon value { false } if (is_internal.value) { change preference_items_list value { "" } } else { @@ -136,6 +138,7 @@ is_internal: f.is_internal, item_name: f.item_name, item_icon: f.item_icon, + external_icon: f.external_icon, preference_items_list: f.preference_items_list, item_icon_preview: f.item_icon_preview, external_url: f.external_url, @@ -149,7 +152,7 @@ make [preference_items_list], editable on trans when { true } make [external_url], hidden on trans when { true } } else { - make [item_icon, item_name, external_url], editable on trans when { true } + make [item_icon, item_name, external_url, external_icon], editable on trans when { true } make item_icon_preview, visible on trans when { true } make [inherit_name, inherit_icon, preference_items_list, preference_item_taskRef, filter_divider], hidden on trans when { true } } @@ -162,24 +165,35 @@ <title name="external_url">External URL Link for external website URL + + external_icon + Icon from URL + Icon will be loaded from external website URL + item_icon Item icon identifier Material icon identifier. List of icons with identifiers is available online. Icon will be displayed in dashboard. + external_icon: f.external_icon, icon: f.this, iconPreview: f.item_icon_preview; - changeCaseProperty "icon" about { icon.value } - if (icon.value == "") { change iconPreview value {"""]]>} return; } - - change iconPreview value { - """]]> + icon.value + """]]> + if (external_icon.value) { + change iconPreview value { + + icon.value + """]]> + } + } else { + changeCaseProperty "icon" about { icon.value } + change iconPreview value { + """]]> + icon.value + """]]> + } } + @@ -205,12 +219,13 @@ inherit_icon: f.inherit_icon, item_icon_preview: f.item_icon_preview, + external_icon: f.external_icon, item_icon: f.item_icon, preference_items_list: f.preference_items_list, trans: t.configuration; if (inherit_icon.value) { - make [item_icon, item_icon_preview], visible on trans when { true } + make [item_icon, item_icon_preview, external_icon], visible on trans when { true } if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { def preferenceItemCase = workflowService.findOne(preference_items_list.value) change item_icon value { preferenceItemCase.dataSet["menu_icon"].value } @@ -218,7 +233,7 @@ change item_icon value { "" } } } else { - make item_icon, editable on trans when { true } + make [item_icon, external_icon], editable on trans when { true } make item_icon_preview, visible on trans when { true } change item_icon value { "" } } @@ -298,6 +313,8 @@ Odkaz na externú webovú adresu URL Identifikátor ikony položky Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. Ikona sa zobrazí v panely. + Ikona z adresy URL + Ikona bude načítaná z externej URL adresy Názov položky Názov položky, ktorá sa zobrazí v panely Prebrať ikonu @@ -317,6 +334,8 @@ Link für externe Website-URL Bezeichner des Artikelsymbols Kennung der Materialikone. Die Liste der Symbole mit den Bezeichnungen ist online verfügbar. Das Symbol wird im Dashboard angezeigt. + Symbol aus URL + Das Symbol wird von einer externen Website-URL geladen Name des Gegenstands Artikelbezeichnung, die im Dashboard angezeigt wird Symbol vererben @@ -446,13 +465,27 @@ visible + + 0 + 1 + 1 + 2 + + outline + + + + external_icon + + visible + 2 - 0 + 1 1 1 - outline + standard @@ -462,7 +495,7 @@ 3 - 0 + 1 1 1 @@ -476,7 +509,7 @@ 0 - 1 + 2 1 4 @@ -490,7 +523,7 @@ 0 - 2 + 3 1 4 diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml index c0e5baed459..f4b6354f650 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_management.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -26,8 +26,8 @@ default - false - false + true + true true @@ -63,11 +63,17 @@ - + dashboard_name Dashboard name Dashboard name to be displayed in application dashboard + + dashboard_logo + Dashboard logo + Dashboard logo to be displayed in application dashboard + assets/img/netgrif_full_white.svg + dashboard_item_list Dashboard items @@ -76,6 +82,10 @@ list + + dashboard_item_to_preference_item + + </data> <data type="button"> <id>order_down</id> <title/> @@ -88,20 +98,26 @@ <id>order_down_set</id> <actions phase="post"> <action> + dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, dashboard_item_list: f.dashboard_item_list; if (dashboard_item_list.value == null || dashboard_item_list.value == "") { return } - def keys = dashboard_item_list.options.keySet() as List - def currentIndex = keys.indexOf(dashboard_item_list.value) - if (currentIndex == keys.size() - 1) { + def keysDashboard = dashboard_item_list.options.keySet() as List + def keysPreference = dashboard_item_to_preference_item.options.keySet() as List + def currentIndex = keysDashboard.indexOf(dashboard_item_list.value) + if (currentIndex == keysDashboard.size() - 1) { return } - java.util.Collections.swap(keys, currentIndex, currentIndex + 1) - def newOrder = [:] - keys.forEach { newOrder[it] = dashboard_item_list.options[it]} - change dashboard_item_list options { newOrder } + java.util.Collections.swap(keysDashboard, currentIndex, currentIndex + 1) + java.util.Collections.swap(keysPreference, currentIndex, currentIndex + 1) + def newOrderDashboard = [:] + def newOrderPreference = [:] + keysDashboard.forEach { newOrderDashboard[it] = dashboard_item_list.options[it]} + keysPreference.forEach { newOrderPreference[it] = dashboard_item_to_preference_item.options[it]} + change dashboard_item_list options { newOrderDashboard } + change dashboard_item_to_preference_item options { newOrderPreference } </action> </actions> </event> @@ -118,20 +134,26 @@ <id>order_up_set</id> <actions phase="post"> <action> + dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, dashboard_item_list: f.dashboard_item_list; if (dashboard_item_list.value == null || dashboard_item_list.value == "") { return } - def keys = dashboard_item_list.options.keySet() as List - def currentIndex = keys.indexOf(dashboard_item_list.value) + def keysDashboard = dashboard_item_list.options.keySet() as List + def keysPreference = dashboard_item_to_preference_item.options.keySet() as List + def currentIndex = keysDashboard.indexOf(dashboard_item_list.value) if (currentIndex == 0) { return } - java.util.Collections.swap(keys, currentIndex, currentIndex - 1) - def newOrder = [:] - keys.forEach { newOrder[it] = dashboard_item_list.options[it]} - change dashboard_item_list options { newOrder } + java.util.Collections.swap(keysDashboard, currentIndex, currentIndex - 1) + java.util.Collections.swap(keysPreference, currentIndex, currentIndex - 1) + def newOrderDashboard = [:] + def newOrderPreference = [:] + keysDashboard.forEach { newOrderDashboard[it] = dashboard_item_list.options[it]} + keysPreference.forEach { newOrderPreference[it] = dashboard_item_to_preference_item.options[it]} + change dashboard_item_list options { newOrderDashboard } + change dashboard_item_to_preference_item options { newOrderPreference } </action> </actions> </event> @@ -186,6 +208,7 @@ <id>add_new_item_set</id> <actions phase="post"> <action> + dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, dashboard_item_list: f.dashboard_item_list, existing_menu_items: f.existing_menu_items; @@ -196,6 +219,15 @@ dashboardItemOptions.put(existing_menu_items.value, existing_menu_items.options.get(existing_menu_items.value)) change dashboard_item_list options { dashboardItemOptions } + def dashboardItemCase = workflowService.findOne(existing_menu_items.value) + def preferenceItemOptions = dashboard_item_to_preference_item.options + if (dashboardItemCase.dataSet["is_internal"].value) { + preferenceItemOptions.put(existing_menu_items.value, dashboardItemCase.dataSet["preference_items_list"].value) + } else { + preferenceItemOptions.put(existing_menu_items.value, null) + } + change dashboard_item_to_preference_item options { preferenceItemOptions } + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) @@ -213,6 +245,7 @@ <id>remove_new_item_set</id> <actions phase="post"> <action> + dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, dashboard_item_list: f.dashboard_item_list, existing_menu_items: f.existing_menu_items; @@ -223,6 +256,10 @@ dashboardItemOptions.keySet().removeAll([dashboard_item_list.value]) change dashboard_item_list options { dashboardItemOptions } + def preferenceItemOptions = dashboard_item_to_preference_item.options + preferenceItemOptions.keySet().removeAll([dashboard_item_list.value]) + change dashboard_item_to_preference_item options { preferenceItemOptions } + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) @@ -251,6 +288,30 @@ <property key="fontSize">18</property> </component> </data> + <data type="boolean" immediate="true"> + <id>simple_dashboard_toolbar</id> + <title name="simple_dashboard_toolbar">Simple dashboard toolbar + Should dashboard toolbar contains menu with options + true + + + profile_dashboard_toolbar + Profile in dashboard toolbar + Should dashboard toolbar contains profile button + false + + + language_dashboard_toolbar + Language select in dashboard toolbar + Should dashboard toolbar contains menu with language selection + false + + + logout_dashboard_toolbar + Logout in dashboard toolbar + Should dashboard toolbar contains logout button + false + @@ -258,22 +319,44 @@ Unikátny identifikátor panelu Názov panelu Názov panelu, ktorý bude zobrazený v panely aplikácie + Logo panelu + Logo panelu, ktoré bude zobrazené v panely aplikácie Položky panelu Zoznam položiek panalu, ktoré budú zobrazené v panely aplikácie Existujúce položky panelu Zoznam všetkých existujúcich položiek panelu, ktoré budú zobrazené v panely aplikácie Konfigurácia panela + + Jednoduchý panel nástrojov + Panel nástrojov by mal obsahovať ponuku s možnosťami + Profil na paneli nástrojov + Mal by panel nástrojov obsahovať tlačidlo profilu + Výber jazyka na paneli nástrojov + Mal by panel nástrojov obsahovať menu s výberom jazyka + Odhlásenie na paneli nástrojov + Mal by panel nástrojov obsahovať tlačidlo na odhlásenie Dashboard-ID Eindeutiger Bezeichner des Dashboards Name des Dashboards Name des Dashboards, der im Dashboard der Anwendung angezeigt werden soll + Logo für das Armaturenbrett + Dashboard-Logo, das im Dashboard der Anwendung angezeigt werden soll Dashboard-Elemente Liste der Dashboard-Elemente, die im Dashboard der Anwendung angezeigt werden sollen Vorhandene Dashboard-Elemente Liste aller vorhandenen Dashboard-Elemente, die dem Dashboard hinzugefügt werden können Konfiguration des Dashboards + + Einfache Dashboard-Symbolleiste + Die Symbolleiste des Dashboards sollte ein Menü mit Optionen enthalten + Profil in der Dashboard-Symbolleiste + Die Symbolleiste des Dashboards sollte eine Profilschaltfläche enthalten + Sprachauswahl in der Dashboard-Symbolleiste + Sollte die Symbolleiste des Dashboards ein Menü mit Sprachauswahl enthalten + Abmelden in der Dashboard-Symbolleiste + Die Dashboard-Symbolleiste sollte eine Abmeldeschaltfläche enthalten @@ -316,15 +399,85 @@ editable + + 0 + 1 + 1 + 2 + + outline + + + + dashboard_logo + + editable + 2 - 0 + 1 1 2 outline + + simple_dashboard_toolbar + + editable + + + 0 + 2 + 1 + 1 + + outline + + + + profile_dashboard_toolbar + + editable + + + 1 + 2 + 1 + 1 + + outline + + + + language_dashboard_toolbar + + editable + + + 2 + 2 + 1 + 1 + + outline + + + + logout_dashboard_toolbar + + editable + + + 3 + 2 + 1 + 1 + + outline + + dashboard_item_list @@ -332,7 +485,7 @@ 0 - 1 + 3 2 2 @@ -346,7 +499,7 @@ 2 - 1 + 3 1 1 @@ -360,7 +513,7 @@ 2 - 2 + 4 1 1 @@ -374,7 +527,7 @@ 3 - 1 + 3 1 1 @@ -388,7 +541,7 @@ 0 - 3 + 5 1 2 @@ -402,7 +555,7 @@ 2 - 3 + 5 1 2 @@ -416,7 +569,7 @@ 0 - 4 + 6 1 4 @@ -430,7 +583,7 @@ 0 - 5 + 7 1 4 From c7cb0d2419fbf4d1d105c0854b40fe0f0d901cde Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Fri, 7 Feb 2025 20:11:43 +0100 Subject: [PATCH 005/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - added option to change icon and font colors in dashboard items - fix for items ordering --- .../engine-processes/dashboard_item.xml | 129 +++++++++++++++++- .../engine-processes/dashboard_management.xml | 12 ++ 2 files changed, 134 insertions(+), 7 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index a284be6ebcc..18aae62b294 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -86,6 +86,9 @@ preference_items_list_set + icon_color: f.icon_color, + font_color: f.font_color, + font_weight: f.font_weight, inherit_name: f.inherit_name, inherit_icon: f.inherit_icon, preference_item_taskRef: f.preference_item_taskRef, @@ -96,12 +99,12 @@ change inherit_icon value { true } change inherit_name value { true } if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { - make [inherit_name, inherit_icon], editable on trans when { true } + make [inherit_name, inherit_icon, icon_color, font_color, font_weight], editable on trans when { true } make [preference_item_taskRef, filter_divider], visible on trans when { true } def preferenceItemCase = workflowService.findOne(preference_items_list.value) change preference_item_taskRef value { [preferenceItemCase.tasks.find { it.transition == "item_settings" }?.task] } } else { - make [inherit_name, inherit_icon], visible on trans when { true } + make [inherit_name, inherit_icon, icon_color, font_color, font_weight], visible on trans when { true } make [preference_item_taskRef, filter_divider], hidden on trans when { true } change preference_item_taskRef value { [] } } @@ -135,6 +138,8 @@ } + font_color: f.font_color, + font_weight: f.font_weight, is_internal: f.is_internal, item_name: f.item_name, item_icon: f.item_icon, @@ -152,7 +157,7 @@ make [preference_items_list], editable on trans when { true } make [external_url], hidden on trans when { true } } else { - make [item_icon, item_name, external_url, external_icon], editable on trans when { true } + make [item_icon, item_name, external_url, external_icon, font_color, font_weight], editable on trans when { true } make item_icon_preview, visible on trans when { true } make [inherit_name, inherit_icon, preference_items_list, preference_item_taskRef, filter_divider], hidden on trans when { true } } @@ -169,12 +174,56 @@ external_icon Icon from URL Icon will be loaded from external website URL + + external_icon_set + + + preference_items_list: f.preference_items_list, + inherit_icon: f.inherit_icon, + external_icon: f.external_icon, + icon_color: f.icon_color, + item_icon: f.item_icon, + is_internal: f.is_internal, + trans: t.configuration; + + if (!inherit_icon.value || !is_internal.value) { + change item_icon value { "" } + } + if (external_icon.value) { + make icon_color, visible on trans when { true } + } else if (!(is_internal.value && (preference_items_list.value == null || !org.bson.types.ObjectId.isValid(preference_items_list.value)))) { + make icon_color, editable on trans when { true } + } + + + + + + icon_color + Icon color + Icon color of item. Valid CSS property value font-color. + #0790ff + + icon_color_set + + + icon_color: f.icon_color, + icon: f.item_icon, + iconPreview: f.item_icon_preview; + + change iconPreview value { + + icon_color.value + """]]> + icon.value + """]]> + } + + + item_icon Item icon identifier Material icon identifier. List of icons with identifiers is available online. Icon will be displayed in dashboard. + icon_color: f.icon_color, external_icon: f.external_icon, icon: f.this, iconPreview: f.item_icon_preview; @@ -188,9 +237,8 @@ + icon.value + """]]> } } else { - changeCaseProperty "icon" about { icon.value } change iconPreview value { - """]]> + icon.value + """]]> + + icon_color.value + """]]> + icon.value + """]]> } } @@ -203,6 +251,18 @@ htmltextarea + + font_color + Font color + Font color of item name. Valid CSS property value font-color. + #0790ff + + + font_weight + Font weight + Font weight of item name. Valid CSS property value font-weight. + 400 + item_name Item name @@ -224,6 +284,7 @@ preference_items_list: f.preference_items_list, trans: t.configuration; + change external_icon value { false } if (inherit_icon.value) { make [item_icon, item_icon_preview, external_icon], visible on trans when { true } if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { @@ -315,6 +376,12 @@ Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. Ikona sa zobrazí v panely. Ikona z adresy URL Ikona bude načítaná z externej URL adresy + Farba ikony + Farba ikony položky. Valídna CSS hodnota font-color. + Farba písma + Farba písma názvu položky. Valídna CSS hodnota font-color. + Hrúbka písma + Hrúbka písma menu položky. Valídna CSS hodnota font-weight. Názov položky Názov položky, ktorá sa zobrazí v panely Prebrať ikonu @@ -336,6 +403,12 @@ Kennung der Materialikone. Die Liste der Symbole mit den Bezeichnungen ist online verfügbar. Das Symbol wird im Dashboard angezeigt. Symbol aus URL Das Symbol wird von einer externen Website-URL geladen + Farbe des Symbols + Symbolfarbe des Elements. Gültiger CSS-Eigenschaftswert font-color. + Schriftfarbe + Schriftfarbe des Artikelnamens. Gültiger CSS-Eigenschaftswert font-color. + Gewicht der Schriftart + Schriftstärke des Artikelnamens. Gültiger CSS-Eigenschaftswert font-weight. Name des Gegenstands Artikelbezeichnung, die im Dashboard angezeigt wird Symbol vererben @@ -460,6 +533,34 @@ outline + + font_color + + visible + + + 2 + 0 + 1 + 1 + + outline + + + + font_weight + + visible + + + 3 + 0 + 1 + 1 + + outline + + item_icon @@ -469,7 +570,7 @@ 0 1 1 - 2 + 1 outline @@ -480,7 +581,7 @@ visible - 2 + 1 1 1 1 @@ -488,6 +589,20 @@ standard + + icon_color + + visible + + + 2 + 1 + 1 + 1 + + outline + + item_icon_preview diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml index f4b6354f650..a08c114f8bc 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_management.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -99,6 +99,7 @@ dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + items_order: f.items_order, dashboard_item_list: f.dashboard_item_list; if (dashboard_item_list.value == null || dashboard_item_list.value == "") { @@ -118,6 +119,7 @@ keysPreference.forEach { newOrderPreference[it] = dashboard_item_to_preference_item.options[it]} change dashboard_item_list options { newOrderDashboard } change dashboard_item_to_preference_item options { newOrderPreference } + change items_order value { String.join(",", newOrderDashboard.keySet()) } @@ -135,6 +137,7 @@ dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + items_order: f.items_order, dashboard_item_list: f.dashboard_item_list; if (dashboard_item_list.value == null || dashboard_item_list.value == "") { @@ -154,6 +157,7 @@ keysPreference.forEach { newOrderPreference[it] = dashboard_item_to_preference_item.options[it]} change dashboard_item_list options { newOrderDashboard } change dashboard_item_to_preference_item options { newOrderPreference } + change items_order value { String.join(",", newOrderDashboard.keySet()) } @@ -209,6 +213,7 @@ dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + items_order: f.items_order, dashboard_item_list: f.dashboard_item_list, existing_menu_items: f.existing_menu_items; @@ -218,6 +223,7 @@ def dashboardItemOptions = dashboard_item_list.options dashboardItemOptions.put(existing_menu_items.value, existing_menu_items.options.get(existing_menu_items.value)) change dashboard_item_list options { dashboardItemOptions } + change items_order value { String.join(",", dashboardItemOptions.keySet()) } def dashboardItemCase = workflowService.findOne(existing_menu_items.value) def preferenceItemOptions = dashboard_item_to_preference_item.options @@ -247,6 +253,7 @@ dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, dashboard_item_list: f.dashboard_item_list, + items_order: f.items_order, existing_menu_items: f.existing_menu_items; if (dashboard_item_list.value == null || !org.bson.types.ObjectId.isValid(dashboard_item_list.value)) { @@ -255,6 +262,7 @@ def dashboardItemOptions = dashboard_item_list.options dashboardItemOptions.keySet().removeAll([dashboard_item_list.value]) change dashboard_item_list options { dashboardItemOptions } + change items_order value { String.join(",", dashboardItemOptions.keySet()) } def preferenceItemOptions = dashboard_item_to_preference_item.options preferenceItemOptions.keySet().removeAll([dashboard_item_list.value]) @@ -312,6 +320,10 @@ Should dashboard toolbar contains logout button false + + items_order + + </data> <!-- I18NS --> <i18n locale="sk"> From 16a3cd670c2d1e5f4397360b3cbe12a92174d8e7 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 10 Feb 2025 10:07:26 +0100 Subject: [PATCH 006/174] [NAE-2051] Implementovat moznost volby komponentu zobrazenia v menu itemoch - fix ActionDelegate.execute method - update ActionDelegate menu item methods - import configuration nets in FilterRunner - implement MenuItemService with new or updated domain models - move menu item domain models to menu package - implement MenuItemView enum - implement MenuItemUtils - rename preference_item to menu_item - add tabbed_case_view_configuration and tabbed_task_view_configuration nets - update MenuItemApiTest according to changes --- .../logic/action/ActionDelegate.groovy | 689 ++--- .../engine/startup/FilterRunner.groovy | 21 +- .../engine/menu/domain/FilterBody.java | 67 + .../domain/menu => menu/domain}/Menu.java | 2 +- .../menu => menu/domain}/MenuAndFilters.java | 2 +- .../menu => menu/domain}/MenuEntry.java | 2 +- .../menu => menu/domain}/MenuEntryRole.java | 2 +- .../engine/menu/domain/MenuItemBody.java | 181 ++ .../engine/menu/domain/MenuItemConstants.java | 35 + .../engine/menu/domain/MenuItemView.java | 67 + .../domain/menu => menu/domain}/MenuList.java | 2 +- .../engine/menu/domain/ToDataSetOutcome.java | 53 + .../configurations/TabbedCaseViewBody.java | 80 + .../TabbedCaseViewConstants.java | 22 + .../configurations/TabbedTaskViewBody.java | 63 + .../TabbedTaskViewConstants.java | 16 + .../menu/domain/configurations/ViewBody.java | 70 + .../domain/configurations/ViewConstants.java | 15 + .../engine/menu/services/MenuItemService.java | 545 ++++ .../services/interfaces/IMenuItemService.java | 50 + .../engine/menu/utils/MenuItemUtils.java | 91 + .../workflow/domain/menu/MenuItemBody.java | 268 -- .../domain/menu/MenuItemConstants.java | 67 - .../service/MenuImportExportService.java | 8 +- .../interfaces/IMenuImportExportService.java | 2 +- .../engine-processes/menu/menu_item.xml | 1491 +++++++++ .../menu/tabbed_case_view_configuration.xml | 933 ++++++ .../menu/tabbed_task_view_configuration.xml | 716 +++++ .../engine-processes/preference_item.xml | 2725 ----------------- .../engine/action/MenuItemApiTest.groovy | 175 +- .../engine/menu/MenuImportExportTest.groovy | 2 +- .../resources/petriNets/filter_api_test.xml | 23 +- 32 files changed, 4867 insertions(+), 3618 deletions(-) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java rename src/main/java/com/netgrif/application/engine/{workflow/domain/menu => menu/domain}/Menu.java (96%) rename src/main/java/com/netgrif/application/engine/{workflow/domain/menu => menu/domain}/MenuAndFilters.java (96%) rename src/main/java/com/netgrif/application/engine/{workflow/domain/menu => menu/domain}/MenuEntry.java (96%) rename src/main/java/com/netgrif/application/engine/{workflow/domain/menu => menu/domain}/MenuEntryRole.java (96%) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java rename src/main/java/com/netgrif/application/engine/{workflow/domain/menu => menu/domain}/MenuList.java (95%) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java create mode 100644 src/main/resources/petriNets/engine-processes/menu/menu_item.xml create mode 100644 src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml create mode 100644 src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml delete mode 100644 src/main/resources/petriNets/engine-processes/preference_item.xml diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index f981a49dcaa..8f1eb13a924 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -17,12 +17,20 @@ import com.netgrif.application.engine.elastic.web.requestbodies.ElasticTaskSearc import com.netgrif.application.engine.export.configuration.ExportConfiguration import com.netgrif.application.engine.export.domain.ExportDataConfig import com.netgrif.application.engine.export.service.interfaces.IExportService -import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService import com.netgrif.application.engine.history.service.IHistoryService +import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService import com.netgrif.application.engine.importer.service.FieldFactory import com.netgrif.application.engine.mail.domain.MailDraft import com.netgrif.application.engine.mail.interfaces.IMailAttemptService import com.netgrif.application.engine.mail.interfaces.IMailService +import com.netgrif.application.engine.menu.domain.FilterBody +import com.netgrif.application.engine.menu.domain.MenuItemBody +import com.netgrif.application.engine.menu.domain.MenuItemConstants +import com.netgrif.application.engine.menu.domain.MenuItemView +import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody +import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody +import com.netgrif.application.engine.menu.domain.configurations.ViewBody +import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.pdf.generator.config.PdfResource import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator @@ -52,8 +60,6 @@ import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.SetDataEventOutcome import com.netgrif.application.engine.workflow.domain.eventoutcomes.taskoutcomes.AssignTaskEventOutcome import com.netgrif.application.engine.workflow.domain.eventoutcomes.taskoutcomes.TaskEventOutcome -import com.netgrif.application.engine.workflow.domain.menu.MenuItemBody -import com.netgrif.application.engine.workflow.domain.menu.MenuItemConstants import com.netgrif.application.engine.workflow.service.FileFieldInputStream import com.netgrif.application.engine.workflow.service.TaskService import com.netgrif.application.engine.workflow.service.interfaces.* @@ -74,10 +80,8 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable -import java.text.Normalizer import java.time.ZoneId import java.util.stream.Collectors - /** * ActionDelegate class contains Actions API methods. */ @@ -190,6 +194,9 @@ class ActionDelegate { @Autowired PublicViewProperties publicViewProperties + @Autowired + IMenuItemService menuItemService + FrontendActionOutcome Frontend /** @@ -580,7 +587,7 @@ class ActionDelegate { }, where: { Closure<Predicate> closure -> [with: { Map dataSet -> - executeTasks(dataSet, taskId, closure) + executeTasks(dataSet, task.stringId, closure) }] }] } @@ -1598,32 +1605,15 @@ class ActionDelegate { @NamedVariant Case createFilter(def title, String query, String type, List<String> allowedNets, String icon, String visibility, def filterMetadata) { - Case filterCase = createCase(FilterRunner.FILTER_PETRI_NET_IDENTIFIER, title as String) - filterCase.setIcon(icon) - filterCase.dataSet[DefaultFiltersRunner.FILTER_I18N_TITLE_FIELD_ID].value = (title instanceof I18nString) ? title : new I18nString(title as String) - filterCase = workflowService.save(filterCase) - Task newFilterTask = findTask { it._id.eq(new ObjectId(filterCase.tasks.find { it.transition == DefaultFiltersRunner.AUTO_CREATE_TRANSITION }.task)) } - assignTask(newFilterTask) - - def setDataMap = [ - (DefaultFiltersRunner.FILTER_TYPE_FIELD_ID) : [ - "type" : "enumeration_map", - "value": type - ], - (DefaultFiltersRunner.FILTER_VISIBILITY_FIELD_ID): [ - "type" : "enumeration_map", - "value": visibility - ], - (DefaultFiltersRunner.FILTER_FIELD_ID) : [ - "type" : "filter", - "value" : query, - "allowedNets" : allowedNets, - "filterMetadata": filterMetadata ?: defaultFilterMetadata(type) - ] - ] - setData(newFilterTask, setDataMap) - finishTask(newFilterTask) - return workflowService.findOne(filterCase.stringId) + FilterBody body = new FilterBody() + body.setTitle((title instanceof I18nString) ? title : new I18nString(title as String)) + body.setQuery(query) + body.setType(type) + body.setAllowedNets(allowedNets) + body.setIcon(icon) + body.setVisibility(visibility) + body.setMetadata(filterMetadata) + return menuItemService.createFilter(body) } /** @@ -1706,7 +1696,7 @@ class ActionDelegate { /** * deletes filter instance - * Note: do not call this method if given instance is referenced in any preference_item instance + * Note: do not call this method if given instance is referenced in any menu_item instance * @param filter * @return */ @@ -1732,15 +1722,15 @@ class ActionDelegate { filter.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString, null ) - body.setFilter(filter) - body.setCaseDefaultHeaders(caseDefaultHeaders) - body.setTaskDefaultHeaders(taskDefaultHeaders) body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseCustomView(false) - body.setCaseRequireTitleInCreation(true) + body.setUseTabbedView(true) - return createMenuItem(body) + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + initializeViewType(body) + + return menuItemService.createMenuItem(body) } /** @@ -1761,15 +1751,16 @@ class ActionDelegate { filter.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString, null ) - body.setFilter(filter) - body.setCaseDefaultHeaders(caseDefaultHeaders) - body.setTaskDefaultHeaders(taskDefaultHeaders) + body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseCustomView(false) - body.setCaseRequireTitleInCreation(true) + body.setUseTabbedView(true) - return createMenuItem(body) + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + initializeViewType(body) + + return menuItemService.createMenuItem(body) } /** @@ -1791,15 +1782,16 @@ class ActionDelegate { filter.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString, null ) - body.setFilter(filter) - body.setCaseDefaultHeaders(caseDefaultHeaders) - body.setTaskDefaultHeaders(taskDefaultHeaders) + body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseCustomView(false) - body.setCaseRequireTitleInCreation(true) + body.setUseTabbedView(true) - return createMenuItem(body) + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + initializeViewType(body) + + return menuItemService.createMenuItem(body) } /** @@ -1820,15 +1812,16 @@ class ActionDelegate { filter.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString, null ) - body.setFilter(filter) - body.setCaseDefaultHeaders(caseDefaultHeaders) - body.setTaskDefaultHeaders(taskDefaultHeaders) + body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseCustomView(false) - body.setCaseRequireTitleInCreation(true) + body.setUseTabbedView(true) - return createMenuItem(body) + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + initializeViewType(body) + + return menuItemService.createMenuItem(body) } /** @@ -1846,7 +1839,7 @@ class ActionDelegate { * @param caseDefaultHeaders List of headers displayed in case view * @param taskDefaultHeaders List of headers displayed in task view * - * @return created Case of preference_item + * @return created Case of menu_item * */ @NamedVariant Case createMenuItem(String uri, String identifier, def name, String icon = "filter_none", Case filter = null, @@ -1858,57 +1851,37 @@ class ActionDelegate { (name instanceof I18nString) ? name : new I18nString(name as String), icon ) - body.setFilter(filter) - body.setCaseDefaultHeaders(caseDefaultHeaders) - body.setTaskDefaultHeaders(taskDefaultHeaders) + body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseCustomView(false) - body.setCaseRequireTitleInCreation(true) + body.setUseTabbedView(true) - return createMenuItem(body) + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + initializeViewType(body) + + return menuItemService.createMenuItem(body) } /** - * Changes data of provided preference_item instance. These attributes can be changed: + * Changes data of provided menu_item instance. These attributes can be changed: * <ul> * <li> <code>changeMenuItem item allowedRoles { ["role_1":"my_process_id"] }</code> * <li> <code>changeMenuItem item bannedRoles { ["role_1":"my_process_id"] }</code> - * <li> <code>changeMenuItem item caseDefaultHeaders { ["meta-title","meta-visualId"] }</code> - * <li> <code>changeMenuItem item taskDefaultHeaders { ["meta-title","meta-caseId"] }</code> - * <li> <code>changeMenuItem item filter { filterCase }</code> * <li> <code>changeMenuItem item uri { "/my_node1/my_node2" }</code> * <li> <code>changeMenuItem item title { new I18nString("New title") }</code> * <li> <code>changeMenuItem item title { "New title" }</code> * <li> <code>changeMenuItem item menuIcon { "filter_alt" }</code> * <li> <code>changeMenuItem item tabIcon { "filter_none" }</code> * </ul> - * @param item {@link Case} instance of preference_item.xml + * @param item {@link Case} instance of menu_item.xml */ def changeMenuItem(Case item) { [allowedRoles : { cl -> - updateMenuItemRoles(item, cl as Closure, MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId) + updateMenuItemRoles(item, cl as Closure, MenuItemConstants.FIELD_ALLOWED_ROLES) }, bannedRoles : { cl -> - updateMenuItemRoles(item, cl as Closure, MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.attributeId) - }, - caseDefaultHeaders : { cl -> - String defaultHeaders = cl() as String - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS.attributeId): ["type": "text", "value": defaultHeaders] - ]) - }, - taskDefaultHeaders : { cl -> - String defaultHeaders = cl() as String - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS.attributeId): ["type": "text", "value": defaultHeaders] - ]) - }, - filter : { cl -> - def filter = cl() as Case - setData("change_filter", item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_NEW_FILTER_ID.attributeId): ["type": "text", "value": filter.stringId] - ]) + updateMenuItemRoles(item, cl as Closure, MenuItemConstants.FIELD_BANNED_ROLES) }, uri : { cl -> def uri = cl() as String @@ -1921,38 +1894,32 @@ class ActionDelegate { title : { cl -> def value = cl() I18nString newName = (value instanceof I18nString) ? value : new I18nString(value as String) - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId): ["type": "i18n", "value": newName] + setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + (MenuItemConstants.FIELD_MENU_NAME): ["type": "i18n", "value": newName] ]) }, menuIcon : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON.attributeId): ["type": "text", "value": value] + setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + (MenuItemConstants.FIELD_MENU_ICON): ["type": "text", "value": value] ]) }, tabIcon : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_ICON.attributeId): ["type": "text", "value": value] - ]) - }, - requireTitleInCreation: { cl -> - def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION.attributeId): ["type": "boolean", "value": value] + setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + (MenuItemConstants.FIELD_TAB_ICON): ["type": "text", "value": value] ]) }, useCustomView : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW.attributeId): ["type": "boolean", "value": value] + setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + (MenuItemConstants.FIELD_USE_CUSTOM_VIEW): ["type": "boolean", "value": value] ]) }, customViewSelector : { cl -> def value = cl() - setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR.attributeId): ["type": "text", "value": value] + setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + (MenuItemConstants.FIELD_CUSTOM_VIEW_SELECTOR): ["type": "text", "value": value] ]) }] @@ -2005,7 +1972,14 @@ class ActionDelegate { List<String> defaultHeaders = [], String icon = "", String visibility = DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE) { - Case filter = createFilter(title, query, type, allowedNets, icon, visibility, null) + FilterBody filterBody = new FilterBody() + filterBody.setTitle((title instanceof I18nString) ? title : new I18nString(title as String)) + filterBody.setQuery(query) + filterBody.setType(type) + filterBody.setAllowedNets(allowedNets) + filterBody.setIcon(icon) + filterBody.setVisibility(visibility) + Case filter = menuItemService.createFilter(filterBody) Case menuItem = createMenuItem(uri, identifier, filter, groupName, allowedRoles, bannedRoles, defaultHeaders) return menuItem } @@ -2033,13 +2007,20 @@ class ActionDelegate { String icon = "", String visibility = DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE, Case orgGroup = null) { - Case filter = createFilter(title, query, type, allowedNets, icon, visibility, null) + FilterBody filterBody = new FilterBody() + filterBody.setTitle((title instanceof I18nString) ? title : new I18nString(title as String)) + filterBody.setQuery(query) + filterBody.setType(type) + filterBody.setAllowedNets(allowedNets) + filterBody.setIcon(icon) + filterBody.setVisibility(visibility) + Case filter = menuItemService.createFilter(filterBody) Case menuItem = createMenuItem(uri, identifier, filter, allowedRoles, bannedRoles, orgGroup, defaultHeaders) return menuItem } /** - * Creates filter and preference_item instances with given parameters. + * Creates filter and menu_item instances with given parameters. * * @param uri resource where the item is located in * @param itemIdentifier unique identifier of item @@ -2059,7 +2040,7 @@ class ActionDelegate { * @param itemTaskDefaultHeaders List of headers displayed in task view * @param filterMetadata metadata for filter. If no value is provided, then default value is used: {@link #defaultFilterMetadata(String)} * - * @return created {@link Case} instance of preference_item + * @return created {@link Case} instance of menu_item * */ @NamedVariant Case createFilterInMenu(String uri, String itemIdentifier, def itemAndFilterName, String filterQuery, @@ -2067,13 +2048,22 @@ class ActionDelegate { String itemAndFilterIcon = "filter_none", Map<String, String> itemAllowedRoles = [:], Map<String, String> itemBannedRoles = [:], List<String> itemCaseDefaultHeaders = [], List<String> itemTaskDefaultHeaders = [], def filterMetadata = null) { - Case filter = createFilter(itemAndFilterName, filterQuery, filterType, filterAllowedNets, itemAndFilterIcon, filterVisibility, filterMetadata) - Case menuItem = createMenuItem(uri, itemIdentifier, itemAndFilterName, itemAndFilterIcon, filter, itemAllowedRoles, itemBannedRoles, itemCaseDefaultHeaders, itemTaskDefaultHeaders) + FilterBody filterBody = new FilterBody() + filterBody.setTitle((itemAndFilterName instanceof I18nString) ? itemAndFilterName : new I18nString(itemAndFilterName as String)) + filterBody.setQuery(filterQuery) + filterBody.setType(filterType) + filterBody.setAllowedNets(filterAllowedNets) + filterBody.setIcon(itemAndFilterIcon) + filterBody.setVisibility(filterVisibility) + filterBody.setMetadata(filterMetadata as Map<String, Object>) + Case filter = menuItemService.createFilter(filterBody) + Case menuItem = createMenuItem(uri, itemIdentifier, itemAndFilterName, itemAndFilterIcon, filter, itemAllowedRoles, + itemBannedRoles, itemCaseDefaultHeaders, itemTaskDefaultHeaders) return menuItem } /** - * Creates filter and preference_item instances with given parameters. + * Creates filter and menu_item instances with given parameters. * * @param body configuration class for menu item creation * @param filterQuery elastic query for filter @@ -2084,99 +2074,65 @@ class ActionDelegate { * @param filterAllowedNets List of allowed nets. Element of list is process identifier * @param filterMetadata metadata for filter. If no value is provided, then default value is used: {@link #defaultFilterMetadata(String)} * - * @return created {@link Case} instance of preference_item + * @return created {@link Case} instance of menu_item * */ Case createFilterInMenu(MenuItemBody body, String filterQuery, String filterType, String filterVisibility, List<String> filterAllowedNets = [], def filterMetadata = null) { - Case filter = createFilter(body.menuName, filterQuery, filterType, filterAllowedNets, body.menuIcon, filterVisibility, filterMetadata) - body.filter = filter + FilterBody filterBody = new FilterBody() + filterBody.setTitle(body.menuName) + filterBody.setQuery(filterQuery) + filterBody.setType(filterType) + filterBody.setAllowedNets(filterAllowedNets) + filterBody.setIcon(body.menuIcon) + filterBody.setVisibility(filterVisibility) + filterBody.setMetadata(filterMetadata as Map<String, Object>) + + body.setView(createLegacyMenuItemViews(filterBody)) + body.setUseTabbedView(true) + Case menuItem = createMenuItem(body) return menuItem } Case createMenuItem(MenuItemBody body) { - String sanitizedIdentifier = sanitize(body.identifier) - - if (existsMenuItem(sanitizedIdentifier)) { - throw new IllegalArgumentException("Menu item identifier $sanitizedIdentifier is not unique!") - } - - Case parentItemCase = getOrCreateFolderItem(body.uri) - I18nString newName = body.menuName ?: (body.filter?.dataSet[FILTER_FIELD_I18N_FILTER_NAME].value as I18nString) - - Case menuItemCase = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, newName?.defaultValue) - menuItemCase.setUriNodeId(uriService.findByUri(body.uri).stringId) - menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId].options = body.allowedRoles - menuItemCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.attributeId].options = body.bannedRoles - if (parentItemCase != null) { - parentItemCase = appendChildCaseIdAndSave(parentItemCase, menuItemCase.stringId) - } - menuItemCase = workflowService.save(menuItemCase) - Task newItemTask = findTask { it._id.eq(new ObjectId(menuItemCase.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task)) } - String nodePath = createNodePath(body.uri, sanitizedIdentifier) - uriService.getOrCreate(nodePath, UriContentType.CASE) - - newItemTask = assignTask(newItemTask) - setData(newItemTask, body.toDataSet(parentItemCase.stringId, nodePath)) - finishTask(newItemTask) - - return workflowService.findOne(menuItemCase.stringId) + return menuItemService.createMenuItem(body) } - protected String sanitize(String input) { - return Normalizer.normalize(input.trim(), Normalizer.Form.NFD) - .replaceAll("[^\\p{ASCII}]", "") - .replaceAll("\\p{InCombiningDiacriticalMarks}+", "") - .replaceAll("[\\W-]+", "-") - .toLowerCase() - } - - protected String createNodePath(String uri, String identifier) { - if (uri == uriService.getUriSeparator()) { - return uri + identifier + protected void initializeViewType(MenuItemBody body) { + if (body.view.filterBody.type == "Case") { + body.setViewType(MenuItemView.TABBED_CASE_VIEW) } else { - return uri + uriService.getUriSeparator() + identifier + body.setViewType(MenuItemView.TABBED_TASK_VIEW) } } - protected Case getOrCreateFolderItem(String uri) { - UriNode node = uriService.getOrCreate(uri, UriContentType.CASE) - MenuItemBody body = new MenuItemBody(new I18nString(node.name), "folder") - return getOrCreateFolderRecursive(node, body) + protected ViewBody createLegacyMenuItemViews(Case filterCase, List<String> caseDefaultHeaders = null, + List<String> taskDefaultHeaders = null) { + FilterBody body = new FilterBody(filterCase) + body.setType((String) filterCase?.getFieldValue("filter_type")) + return createLegacyMenuItemViews(body, caseDefaultHeaders, taskDefaultHeaders) } - protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case childFolderCase = null) { - Case folder = findFolderCase(node) - if (folder != null) { - if (childFolderCase != null) { - folder = appendChildCaseIdAndSave(folder, childFolderCase.stringId) - initializeParentId(childFolderCase, folder.stringId) - } - return folder - } - - folder = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, body.menuName.toString()) - folder.setUriNodeId(node.parentId) - if (childFolderCase != null) { - folder = appendChildCaseIdAndSave(folder, childFolderCase.stringId) - initializeParentId(childFolderCase, folder.stringId) - } else { - folder = workflowService.save(folder) - } - Task newItemTask = findTask { it._id.eq(new ObjectId(folder.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task)) } - assignTask(newItemTask) - setData(newItemTask, body.toDataSet(null, node.uriPath)) - finishTask(newItemTask) + protected ViewBody createLegacyMenuItemViews(FilterBody filterBody, List<String> caseDefaultHeaders = null, + List<String> taskDefaultHeaders = null) { + if (filterBody.getType() == "Case") { + ViewBody caseView = new TabbedCaseViewBody() + caseView.setFilterBody(filterBody) + caseView.setDefaultHeaders(caseDefaultHeaders) + caseView.setRequireTitleInCreation(true) - folder = workflowService.findOne(folder.stringId) - if (node.parentId != null) { - UriNode parentNode = uriService.findById(node.parentId) - body = new MenuItemBody(new I18nString(parentNode.name), "folder") + ViewBody taskView = new TabbedTaskViewBody() + taskView.setDefaultHeaders(taskDefaultHeaders) + caseView.setChainedView(taskView) - getOrCreateFolderRecursive(parentNode, body, folder) + return caseView + } else if (filterBody.getType() == "Task"){ + ViewBody taskView = new TabbedTaskViewBody() + taskView.setFilterBody(filterBody) + taskView.setDefaultHeaders(taskDefaultHeaders) + return taskView } - - return folder + return null } /** @@ -2184,197 +2140,25 @@ class ActionDelegate { * item is moved. Cyclic destination path is forbidden (f.e. from <code>"/my_node"</code> to * <code>"/my_node/my_node2"</code> * - * @param item Instance of preference_item to be moved + * @param item Instance of menu_item to be moved * @param destUri destination path where the item will be moved. F.e. <code>"/my_new_node"</code> * */ void moveMenuItem(Case item, String destUri) { - if (isCyclicNodePath(item, destUri)) { - throw new IllegalArgumentException("Cyclic path not supported. Destination path: ${destUri}") - } - - List<Case> casesToSave = new ArrayList<>() - - List<String> parentIdList = item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList<String> - if (parentIdList != null && parentIdList.size() > 0) { - Case oldParent = removeChildItemFromParent(parentIdList[0], item) - casesToSave.add(oldParent) - } - - UriNode destNode = uriService.getOrCreate(destUri, UriContentType.CASE) - Case newParent = getOrCreateFolderItem(destNode.uriPath) - if (newParent != null) { - item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = [newParent.stringId] as ArrayList - newParent = appendChildCaseId(newParent, item.stringId) - casesToSave.add(newParent) - } else { - item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = null - } - - item.uriNodeId = destNode.stringId - item = resolveAndHandleNewNodePath(item, destNode.uriPath) - casesToSave.add(item) - - if (hasChildren(item)) { - List<Case> childrenToSave = updateNodeInChildrenFoldersRecursive(item) - casesToSave.addAll(childrenToSave) - } - - for (aCase in casesToSave) { - if (aCase != null) { - workflowService.save(aCase) - } - } + menuItemService.moveItem(item, destUri) } /** - * Duplicates menu item. It creates new preference_item instance with the same {@link Case#dataSet} as the provided + * Duplicates menu item. It creates new menu_item instance with the same {@link Case#dataSet} as the provided * item instance. The only difference is in title, menu_item_identifier and associations * * @param originItem Menu item instance, which is duplicated * @param newTitle Title of menu item, that is displayed in menu and tab. Cannot be empty or null. * @param newIdentifier unique menu item identifier * - * @return duplicated {@link Case} instance of preference_item + * @return duplicated {@link Case} instance of menu_item * */ Case duplicateMenuItem(Case originItem, I18nString newTitle, String newIdentifier) { - if (!newIdentifier) { - throw new IllegalArgumentException("View item identifier is null!") - } - if (newTitle == null || newTitle.defaultValue == "") { - throw new IllegalArgumentException("Default title is empty") - } - String sanitizedIdentifier = sanitize(newIdentifier) - if (existsMenuItem(sanitizedIdentifier)) { - throw new IllegalArgumentException("View item identifier $sanitizedIdentifier is not unique!") - } - - Case duplicated = createCase(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, newTitle.defaultValue) - duplicated.uriNodeId = originItem.uriNodeId - duplicated.dataSet = originItem.dataSet - duplicated.title = newTitle.defaultValue - duplicated = workflowService.save(duplicated) - - UriNode node = uriService.findById(originItem.uriNodeId) - String newNodePath = createNodePath(node.uriPath, sanitizedIdentifier) - uriService.getOrCreate(newNodePath, UriContentType.CASE) - - Task newItemTask = findTask { it._id.eq(new ObjectId(duplicated.tasks.find { it.transition == MenuItemConstants.PREFERENCE_ITEM_FIELD_INIT_TRANS_ID.attributeId }.task)) } - Map updatedDataSet = [ - (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId) : [ - "value": null, - "type" : "text" - ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId) : [ - "value": null, - "type" : "text" - ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId) : [ - "value": newTitle, - "type" : "i18n" - ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_NAME.attributeId) : [ - "value": newTitle, - "type" : "i18n" - ], - (MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId) : [ - "value": newNodePath, - "type" : "text" - ], - // Must be reset by button, because we have the same dataSet reference between originItem and duplicated - (MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS.attributeId): [ - "value": 0, - "type" : "button" - ], - ] - assignTask(newItemTask) - dataService.setData(newItemTask, ImportHelper.populateDataset(updatedDataSet)) - finishTask(newItemTask) - - String parentId = (originItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList).get(0) - if (parentId) { - Case parent = workflowService.findOne(parentId) - appendChildCaseIdAndSave(parent, duplicated.stringId) - } - return workflowService.findOne(duplicated.stringId) - } - - private List<Case> updateNodeInChildrenFoldersRecursive(Case parentFolder) { - List<String> childItemIds = parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List<String> - if (childItemIds == null || childItemIds.isEmpty()) { - return new ArrayList<Case>() - } - - List<Case> children = workflowService.findAllById(childItemIds) - - List<Case> casesToSave = new ArrayList<>() - for (child in children) { - UriNode parentNode = uriService.getOrCreate(parentFolder.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId) as String, UriContentType.CASE) - child.uriNodeId = parentNode.stringId - child = resolveAndHandleNewNodePath(child, parentNode.uriPath) - - casesToSave.add(child) - casesToSave.addAll(updateNodeInChildrenFoldersRecursive(child)) - } - - return casesToSave - } - - private Case resolveAndHandleNewNodePath(Case folderItem, String destUri) { - String newNodePath = resolveNewNodePath(folderItem, destUri) - UriNode newNode = uriService.getOrCreate(newNodePath, UriContentType.CASE) - folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value = newNode.uriPath - - return folderItem - } - - private String resolveNewNodePath(Case folderItem, String destUri) { - return destUri + - uriService.getUriSeparator() + - folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId) as String - } - - private Case removeChildItemFromParent(String folderId, Case childItem) { - Case parentFolder = workflowService.findOne(folderId) - (parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List).remove(childItem.stringId) - parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.attributeId].value = hasChildren(parentFolder) - workflowService.save(parentFolder) - } - - private boolean isCyclicNodePath(Case folderItem, String destUri) { - String oldNodePath = folderItem.getFieldValue(MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId) - return destUri.contains(oldNodePath) - } - - private boolean hasChildren(Case folderItem) { - List children = folderItem.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List - return children != null && children.size() > 0 - } - - private Case appendChildCaseIdAndSave(Case folderCase, String childItemCaseId) { - folderCase = appendChildCaseId(folderCase, childItemCaseId) - return workflowService.save(folderCase) - } - - private Case appendChildCaseId(Case folderCase, String childItemCaseId) { - List<String> childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList<String> - if (childIds == null) { - folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value = [childItemCaseId] as ArrayList - } else { - folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value = childIds + [childItemCaseId] as ArrayList - } - - folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.attributeId].value = hasChildren(folderCase) - - return folderCase - } - - private Case initializeParentId(Case childFolderCase, String parentFolderCaseId) { - childFolderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value = [parentFolderCaseId] as ArrayList - return workflowService.save(childFolderCase) - } - - protected Case findFolderCase(UriNode node) { - return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$node.uriPath\"") + return menuItemService.duplicateItem(originItem, newTitle, newIdentifier) } /** @@ -2393,10 +2177,17 @@ class ActionDelegate { * * @param menuItemIdentifier unique menu item identifier * - * @return found preference_item instance. Can be null + * @return found menu_item instance. Can be null */ Case findMenuItem(String menuItemIdentifier) { - return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.menu_item_identifier.textValue.keyword:\"$menuItemIdentifier\"" as String) + return menuItemService.findMenuItem(menuItemIdentifier) + } + + /** + * todo javadoc + * */ + Case findFolderCase(UriNode node) { + return menuItemService.findFolderCase(node) } /** @@ -2407,7 +2198,7 @@ class ActionDelegate { * @return true if the item exists * */ boolean existsMenuItem(String menuItemIdentifier) { - return countCasesElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.menu_item_identifier.fulltextValue.keyword:\"$menuItemIdentifier\"") > 0 + return menuItemService.existsMenuItem(menuItemIdentifier) } /** @@ -2417,13 +2208,7 @@ class ActionDelegate { * @return */ Case findMenuItem(String uri, String name) { - UriNode uriNode = uriService.findByUri(uri) - return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND title.keyword:\"$name\" AND uriNodeId:\"$uriNode.stringId\"") - } - - Case findMenuItemByUriAndIdentifier(String uri, String identifier) { - String nodePath = createNodePath(uri, identifier) - return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"$nodePath\"") + return menuItemService.findMenuItem(uri, name) } /** @@ -2450,18 +2235,6 @@ class ActionDelegate { return findMenuItem(uri, name) } - /** - * Retrieves filter case from preference_item {@link Case} - * - * @param item preference_item instance - * - * @return found filter instance. If not found, <code>null</code> is returned - */ - Case getFilterFromMenuItem(Case item) { - String filterId = (item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_FILTER_CASE.attributeId].value as List)[0] as String - return filterId ? workflowService.findOne(filterId) : null - } - /** * search elastic with string query for first occurrence * @param query @@ -2509,7 +2282,7 @@ class ActionDelegate { Map<String, I18nString> collectRolesForPreferenceItem(Map<String, String> roles) { Map<String, PetriNet> temp = [:] return roles.collectEntries { entry -> - if (entry.value.equals(GLOBAL_ROLE)) { + if (entry.value == GLOBAL_ROLE) { Set<ProcessRole> findGlobalRole = processRoleService.findAllByImportId(ProcessRole.GLOBAL + entry.key) if (findGlobalRole == null || findGlobalRole.isEmpty()) { return @@ -2579,27 +2352,25 @@ class ActionDelegate { } @Deprecated - Case createOrUpdateMenuItem(String id, String uri, String type, String query, String icon, String title, List<String> allowedNets, Map<String, String> roles = [:], Map<String, String> bannedRoles = [:], Case group = null, List<String> defaultHeaders = []) { - Case menuItem = findMenuItem(sanitize(id)) - if (!menuItem) { - Case filter = createFilter(title, query, type, allowedNets, icon, DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE, null) - createUri(uri, UriContentType.DEFAULT) + Case createOrUpdateMenuItem(String id, String uri, String type, String query, String icon, String title, List<String> allowedNets, + Map<String, String> roles = [:], Map<String, String> bannedRoles = [:], Case group = null, + List<String> defaultHeaders = []) { + MenuItemBody body = new MenuItemBody(uri, id, title, icon) + body.setAllowedRoles(collectRolesForPreferenceItem(roles)) + body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) + body.setUseTabbedView(true) - return createMenuItem(uri, id, title, icon, filter, roles, bannedRoles) - } else { - Case filter = getFilterFromMenuItem(menuItem) - changeFilter filter query { query } - changeFilter filter allowedNets { allowedNets } - changeFilter filter title { title } - changeFilter filter icon { icon } - changeMenuItem menuItem allowedRoles { roles } - changeMenuItem menuItem bannedRoles { bannedRoles } - changeMenuItem menuItem defaultHeaders { defaultHeaders.join(",") } - changeMenuItem menuItem uri { uri } - changeMenuItem menuItem filter { filter } - - return workflowService.findOne(menuItem.stringId) - } + FilterBody filterBody = new FilterBody() + filterBody.setTitle(new I18nString(title as String)) + filterBody.setQuery(query) + filterBody.setType(type) + filterBody.setAllowedNets(allowedNets) + filterBody.setIcon(icon) + filterBody.setVisibility(DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE) + + body.setView(createLegacyMenuItemViews(filterBody, defaultHeaders)) + initializeViewType(body) + return menuItemService.createOrUpdateMenuItem(body) } /** @@ -2619,15 +2390,16 @@ class ActionDelegate { * * @return created or updated menu item instance * */ + @Deprecated(since = "6.5.0") Case createOrUpdateMenuItem(String uri, String identifier, def name, String icon = "filter_none", Case filter = null, Map<String, String> allowedRoles = [:], Map<String, String> bannedRoles = [:], List<String> caseDefaultHeaders = [], List<String> taskDefaultHeaders = []) { MenuItemBody body = new MenuItemBody(uri, identifier, name, icon) body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) - body.setCaseDefaultHeaders(caseDefaultHeaders) - body.setTaskDefaultHeaders(taskDefaultHeaders) - body.setFilter(filter) + body.setUseTabbedView(true) + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + initializeViewType(body) return createOrUpdateMenuItem(body) } @@ -2657,6 +2429,7 @@ class ActionDelegate { * * @return created or updated menu item instance along with the actual filter * */ + @Deprecated(since = "6.5.0") Case createOrUpdateMenuItemAndFilter(String uri, String itemIdentifier, def itemAndFilterName, String filterQuery, String filterType, String filterVisibility, List<String> filterAllowedNets = [], String itemAndFilterIcon = "filter_none", Map<String, String> itemAllowedRoles = [:], @@ -2665,11 +2438,20 @@ class ActionDelegate { MenuItemBody body = new MenuItemBody(uri, itemIdentifier, itemAndFilterName, itemAndFilterIcon) body.allowedRoles = collectRolesForPreferenceItem(itemAllowedRoles) body.bannedRoles = collectRolesForPreferenceItem(itemBannedRoles) - body.caseDefaultHeaders = itemCaseDefaultHeaders - body.taskDefaultHeaders = itemTaskDefaultHeaders + body.setUseTabbedView(true) + + FilterBody filterBody = new FilterBody() + filterBody.setTitle((itemAndFilterName instanceof I18nString) ? itemAndFilterName : new I18nString(itemAndFilterName as String)) + filterBody.setQuery(filterQuery) + filterBody.setType(filterType) + filterBody.setAllowedNets(filterAllowedNets) + filterBody.setIcon(itemAndFilterIcon) + filterBody.setVisibility(filterVisibility) + filterBody.setMetadata(filterMetadata as Map<String, Object>) + body.setView(createLegacyMenuItemViews(filterBody, itemCaseDefaultHeaders, itemTaskDefaultHeaders)) + initializeViewType(body) - return createOrUpdateMenuItemAndFilter(body, filterQuery, filterType, filterVisibility, filterAllowedNets, - filterMetadata) + return menuItemService.createOrUpdateMenuItem(body) } /** @@ -2680,12 +2462,7 @@ class ActionDelegate { * @return created or updated menu item instance * */ Case createOrUpdateMenuItem(MenuItemBody body) { - Case item = findMenuItem(sanitize(body.identifier)) - if (item) { - return updateMenuItem(item, body) - } else { - return createMenuItem(body) - } + return menuItemService.createOrUpdateMenuItem(body) } /** @@ -2704,27 +2481,23 @@ class ActionDelegate { * * @return created or updated menu item instance along with the actual filter * */ + @Deprecated(since = "6.5.0") Case createOrUpdateMenuItemAndFilter(MenuItemBody body, String filterQuery, String filterType, String filterVisibility, List<String> filterAllowedNets = [], def filterMetadata = null) { - Case item = findMenuItem(sanitize(body.identifier)) - if (item) { - Case filter = getFilterFromMenuItem(item) - if (filter) { - changeFilter filter query { filterQuery } - changeFilter filter visibility { filterVisibility } - changeFilter filter allowedNets { filterAllowedNets } - changeFilter filter filterMetadata { filterMetadata ?: defaultFilterMetadata(filterType) } - changeFilter filter title { body.menuName } - changeFilter filter icon { body.menuIcon } - } else { - body.filter = createFilter(body.menuName, filterQuery, filterType, filterAllowedNets, body.menuIcon, - filterVisibility, filterMetadata) - } + body.setUseTabbedView(true) - return updateMenuItem(item, body) - } else { - return createFilterInMenu(body, filterQuery, filterType, filterVisibility, filterAllowedNets, filterMetadata) - } + FilterBody filterBody = new FilterBody() + filterBody.setTitle(body.getMenuName()) + filterBody.setQuery(filterQuery) + filterBody.setType(filterType) + filterBody.setAllowedNets(filterAllowedNets) + filterBody.setIcon(body.getMenuIcon()) + filterBody.setVisibility(filterVisibility) + filterBody.setMetadata(filterMetadata as Map<String, Object>) + body.setView(createLegacyMenuItemViews(filterBody)) + initializeViewType(body) + + return menuItemService.createOrUpdateMenuItem(body) } /** @@ -2735,12 +2508,7 @@ class ActionDelegate { * @return created or existing menu item instance * */ Case createOrIgnoreMenuItem(MenuItemBody body) { - Case item = findMenuItem(body.identifier) - if (!item) { - return createMenuItem(body) - } else { - return item - } + return menuItemService.createOrIgnoreMenuItem(body) } /** @@ -2751,22 +2519,24 @@ class ActionDelegate { * * @return created or existing menu item instance * */ + @Deprecated(since = "6.5.0") Case createOrIgnoreMenuItemAndFilter(MenuItemBody body, String filterQuery, String filterType, String filterVisibility, List<String> filterAllowedNets = [], def filterMetadata = null) { - Case item = findMenuItem(body.identifier) - if (!item) { - return createFilterInMenu(body, filterQuery, filterType, filterVisibility, filterAllowedNets, filterMetadata) - } else { - Case filter = getFilterFromMenuItem(item) - if (!filter) { - filter = createFilter(body.menuName, filterQuery, filterType, filterAllowedNets, body.menuIcon, filterVisibility, - filterMetadata) - changeMenuItem item filter { filter } - return workflowService.findOne(item.stringId) - } else { - return item - } - } + body.setUseTabbedView(true) + + FilterBody filterBody = new FilterBody() + filterBody.setTitle(body.getMenuName()) + filterBody.setQuery(filterQuery) + filterBody.setType(filterType) + filterBody.setAllowedNets(filterAllowedNets) + filterBody.setIcon(body.getMenuIcon()) + filterBody.setVisibility(filterVisibility) + filterBody.setMetadata(filterMetadata as Map<String, Object>) + + body.setView(createLegacyMenuItemViews(filterBody)) + initializeViewType(body) + + return menuItemService.createOrIgnoreMenuItem(body) } /** @@ -2778,18 +2548,15 @@ class ActionDelegate { * @return updated menu item instance * */ Case updateMenuItem(Case item, MenuItemBody body) { - def outcome = setData(MenuItemConstants.PREFERENCE_ITEM_SETTINGS_TRANS_ID.attributeId, item, body.toDataSet()) - return outcome.case + return menuItemService.updateMenuItem(item, body) } static Map defaultFilterMetadata(String type) { - return [ - "searchCategories" : [], - "predicateMetadata" : [], - "filterType" : type, - "defaultSearchCategories": true, - "inheritAllowedNets" : false - ] + return FilterBody.getDefaultMetadata(type) + } + + void removeChildItemFromParent(String folderId, Case childItem) { + menuItemService.removeChildItemFromParent(folderId, childItem) } String makeUrl(String publicViewUrl = publicViewProperties.url, String identifier) { diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index 0e1608c6da4..c318595f541 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -1,9 +1,9 @@ package com.netgrif.application.engine.startup +import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.petrinet.domain.PetriNet import com.netgrif.application.engine.petrinet.domain.VersionType import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.workflow.domain.eventoutcomes.petrinetoutcomes.ImportPetriNetEventOutcome import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @@ -24,8 +24,8 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { private static final String FILTER_FILE_NAME = "engine-processes/filter.xml" public static final String FILTER_PETRI_NET_IDENTIFIER = "filter" - private static final String PREFERRED_ITEM_FILE_NAME = "engine-processes/preference_item.xml" - public static final String PREFERRED_ITEM_NET_IDENTIFIER = "preference_item" + private static final String MENU_ITEM_FILE_NAME = "engine-processes/menu/menu_item.xml" + public static final String MENU_NET_IDENTIFIER = "menu_item" private static final String EXPORT_FILTER_FILE_NAME = "engine-processes/export_filters.xml" private static final String EXPORT_NET_IDENTIFIER = "export_filters" @@ -36,7 +36,8 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { @Override void run(String... args) throws Exception { createFilterNet() - createPreferenceItemNet() + createConfigurationNets() + createMenuItemNet() createImportFiltersNet() createExportFiltersNet() } @@ -45,8 +46,16 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { importProcess("Petri net for filters", FILTER_PETRI_NET_IDENTIFIER, FILTER_FILE_NAME) } - Optional<PetriNet> createPreferenceItemNet() { - importProcess("Petri net for filter preferences", PREFERRED_ITEM_NET_IDENTIFIER, PREFERRED_ITEM_FILE_NAME) + Optional<PetriNet> createMenuItemNet() { + importProcess("Petri net for filter preferences", MENU_NET_IDENTIFIER, MENU_ITEM_FILE_NAME) + } + + List<PetriNet> createConfigurationNets() { + return MenuItemView.values().each { view -> + String processIdentifier = view.getIdentifier() + "_configuration" + String filePath = String.format("engine-processes/menu/%s.xml", processIdentifier) + importProcess(String.format("Petri net for %s", processIdentifier), processIdentifier, filePath) + }.collect() } Optional<PetriNet> createImportFiltersNet() { diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java new file mode 100644 index 00000000000..0232fbacb49 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java @@ -0,0 +1,67 @@ +package com.netgrif.application.engine.menu.domain; + +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import com.netgrif.application.engine.startup.DefaultFiltersRunner; +import com.netgrif.application.engine.workflow.domain.Case; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +@NoArgsConstructor +public class FilterBody { + private Case filter; + private I18nString title; + private String query; + private String type; + private List<String> allowedNets; + private String icon; + private String visibility; + private Map<String, Object> metadata; + + public FilterBody(Case filterCase) { + this.filter = filterCase; + } + + /** + * todo javadoc + * */ + public static Map<String, Object> getDefaultMetadata(String type) { + Map<String, Object> resultMap = new HashMap<>(); + + resultMap.put("searchCategories", List.of()); + resultMap.put("predicateMetadata", List.of()); + resultMap.put("filterType", type); + resultMap.put("defaultSearchCategories", true); + resultMap.put("inheritAllowedNets", false); + + return resultMap; + } + + /** + * todo javadoc + * */ + public ToDataSetOutcome toDataSet() { + ToDataSetOutcome outcome = new ToDataSetOutcome(); + + outcome.putDataSetEntry(DefaultFiltersRunner.FILTER_TYPE_FIELD_ID, FieldType.ENUMERATION_MAP, this.type); + outcome.putDataSetEntry(DefaultFiltersRunner.FILTER_VISIBILITY_FIELD_ID, FieldType.ENUMERATION_MAP, this.visibility); + outcome.putDataSetEntry(DefaultFiltersRunner.FILTER_I18N_TITLE_FIELD_ID, FieldType.I18N, this.title); + Map<String, Object> metadata = this.metadata; + if (metadata == null) { + metadata = getDefaultMetadata(this.type); + } + outcome.getDataSet().put(DefaultFiltersRunner.FILTER_FIELD_ID, Map.of( + "type", "filter", + "value", this.query, + "allowedNets", this.allowedNets, + "filterMetadata", metadata + )); + + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/Menu.java b/src/main/java/com/netgrif/application/engine/menu/domain/Menu.java similarity index 96% rename from src/main/java/com/netgrif/application/engine/workflow/domain/menu/Menu.java rename to src/main/java/com/netgrif/application/engine/menu/domain/Menu.java index 62e75dcae2c..cce9388ba21 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/Menu.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/Menu.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.workflow.domain.menu; +package com.netgrif.application.engine.menu.domain; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuAndFilters.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuAndFilters.java similarity index 96% rename from src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuAndFilters.java rename to src/main/java/com/netgrif/application/engine/menu/domain/MenuAndFilters.java index 13b77dbc185..e12f019fabe 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuAndFilters.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuAndFilters.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.workflow.domain.menu; +package com.netgrif.application.engine.menu.domain; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuEntry.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuEntry.java similarity index 96% rename from src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuEntry.java rename to src/main/java/com/netgrif/application/engine/menu/domain/MenuEntry.java index 40bb87973aa..c3900ddf511 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuEntry.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuEntry.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.workflow.domain.menu; +package com.netgrif.application.engine.menu.domain; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuEntryRole.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuEntryRole.java similarity index 96% rename from src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuEntryRole.java rename to src/main/java/com/netgrif/application/engine/menu/domain/MenuEntryRole.java index 66d6aab50e3..dd39ea86e8d 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuEntryRole.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuEntryRole.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.workflow.domain.menu; +package com.netgrif.application.engine.menu.domain; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.netgrif.application.engine.workflow.domain.AuthorizationType; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java new file mode 100644 index 00000000000..852c7863587 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -0,0 +1,181 @@ +package com.netgrif.application.engine.menu.domain; + +import com.netgrif.application.engine.menu.domain.configurations.ViewBody; +import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; +import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate; +import com.netgrif.application.engine.workflow.domain.Case; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * todo javadoc + * Class, that holds configurable attributes of menu item. In case of attribute addition, please update also + * {@link MenuItemBody#toDataSet(String, String, boolean)} method. + */ +@Data +@NoArgsConstructor +public class MenuItemBody { + private String uri; + private String identifier; + + private String menuIcon = "filter_none"; + private I18nString menuName; + private Map<String, I18nString> allowedRoles; + private Map<String, I18nString> bannedRoles; + private boolean useCustomView = false; + private String customViewSelector; + private boolean isAutoSelect = false; + + private boolean useTabbedView; + private String tabIcon; + private boolean useTabIcon = true; + private I18nString tabName; + private MenuItemView viewType; + private ViewBody view; + + public MenuItemBody(I18nString name, String icon) { + this.menuName = name; + this.tabName = name; + this.menuIcon = icon; + this.tabIcon = icon; + } + + public MenuItemBody(I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { + this.menuName = menuName; + this.tabName = tabName; + this.menuIcon = menuIcon; + this.tabIcon = tabIcon; + } + + public MenuItemBody(String uri, String identifier, I18nString name, String icon) { + this.uri = uri; + this.identifier = identifier; + this.menuName = name; + this.tabName = name; + this.menuIcon = icon; + this.tabIcon = icon; + } + + public MenuItemBody(String uri, String identifier, I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { + this.uri = uri; + this.identifier = identifier; + this.menuName = menuName; + this.tabName = tabName; + this.menuIcon = menuIcon; + this.tabIcon = tabIcon; + } + + public MenuItemBody(String uri, String identifier, String name, String icon) { + this.uri = uri; + this.identifier = identifier; + this.menuName = new I18nString(name); + this.tabName = new I18nString(name); + this.menuIcon = icon; + this.tabIcon = icon; + } + + public MenuItemBody(String uri, String identifier, String menuName, String tabName, String menuIcon, String tabIcon) { + this.uri = uri; + this.identifier = identifier; + this.menuName = new I18nString(menuName); + this.tabName = new I18nString(tabName); + this.menuIcon = menuIcon; + this.tabIcon = tabIcon; + } + + public String getIdentifier() { + return MenuItemUtils.sanitize(this.identifier); + } + + public void setMenuName(I18nString name) { + this.menuName = name; + } + + public void setMenuName(String name) { + this.menuName = new I18nString(name); + } + + public void setTabName(I18nString name) { + this.tabName = name; + } + + public void setTabName(String name) { + this.tabName = new I18nString(name); + } + + /** + * todo javadoc + * */ + public boolean hasView() { + return this.view != null; + } + + /** + * todo javadoc + * Transforms attributes into dataSet for {@link ActionDelegate#setData} + * + * @return created dataSet from attributes + */ + public ToDataSetOutcome toDataSet() { + return toDataSet(null, null, null); + } + + /** + * todo javadoc + */ + public ToDataSetOutcome toDataSet(Case viewCase) { + return toDataSet(null, null, viewCase); + } + + /** + * todo javadoc + * Transforms attributes into dataSet for {@link ActionDelegate#setData} + * + * @param parentId id of parent menu item instance + * @param nodePath uri, that represents the menu item (f.e.: "/myItem1/myItem2") + * @return created dataSet from attributes + */ + public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCase) { + ToDataSetOutcome outcome = new ToDataSetOutcome(); + + if (parentId != null) { + outcome.putDataSetEntry(MenuItemConstants.FIELD_PARENT_ID, FieldType.CASE_REF, List.of(parentId)); + } + if (nodePath != null) { + outcome.putDataSetEntry(MenuItemConstants.FIELD_NODE_PATH, FieldType.TEXT, nodePath); + } + outcome.putDataSetEntry(MenuItemConstants.FIELD_MENU_NAME, FieldType.I18N, this.menuName); + outcome.putDataSetEntry(MenuItemConstants.FIELD_MENU_ICON, FieldType.TEXT, this.menuIcon); + outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TABBED_VIEW, FieldType.BOOLEAN, this.useTabbedView); + outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_NAME, FieldType.I18N, this.tabName); + outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_ICON, FieldType.TEXT, this.tabIcon); + if (this.identifier != null) { + outcome.putDataSetEntry(MenuItemConstants.FIELD_IDENTIFIER, FieldType.TEXT, this.getIdentifier()); + } + outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TAB_ICON, FieldType.BOOLEAN, this.useTabIcon); + outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_CUSTOM_VIEW, FieldType.BOOLEAN, + this.useCustomView); + outcome.putDataSetEntry(MenuItemConstants.FIELD_CUSTOM_VIEW_SELECTOR, FieldType.TEXT, + this.customViewSelector); + outcome.putDataSetEntry(MenuItemConstants.FIELD_IS_AUTO_SELECT, FieldType.BOOLEAN, this.isAutoSelect); + outcome.putDataSetEntryOptions(MenuItemConstants.FIELD_ALLOWED_ROLES, FieldType.MULTICHOICE_MAP, this.allowedRoles); + outcome.putDataSetEntryOptions(MenuItemConstants.FIELD_BANNED_ROLES, FieldType.MULTICHOICE_MAP, this.bannedRoles); + + if (viewCase != null) { + outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, + this.viewType.getIdentifier()); + outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID, FieldType.CASE_REF, + List.of(viewCase.getStringId())); + String taskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_SETTINGS_ID); + outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_FORM, FieldType.TASK_REF, List.of(taskId)); + } + + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java new file mode 100644 index 00000000000..d6ac1a37afc --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -0,0 +1,35 @@ +package com.netgrif.application.engine.menu.domain; + +/** + * Here are declared constants of process menu_item.xml. + */ +public class MenuItemConstants { + public static final String FIELD_PARENT_ID = "parentId"; + public static final String FIELD_CHILD_ITEM_IDS = "childItemIds"; + public static final String FIELD_HAS_CHILDREN = "hasChildren"; + public static final String FIELD_IDENTIFIER = "menu_item_identifier"; + public static final String FIELD_APPEND_MENU_ITEM = "append_menu_item_stringId"; + public static final String FIELD_ALLOWED_ROLES = "allowed_roles"; + public static final String FIELD_BANNED_ROLES = "banned_roles"; + public static final String FIELD_MENU_NAME = "menu_name"; + public static final String FIELD_MENU_ICON = "menu_icon"; + public static final String FIELD_TAB_NAME = "tab_name"; + public static final String FIELD_USE_TABBED_VIEW = "use_tabbed_view"; + public static final String FIELD_USE_TAB_ICON = "use_tab_icon"; + public static final String FIELD_TAB_ICON = "tab_icon"; + public static final String FIELD_VIEW_CONFIGURATION_TYPE = "view_configuration_type"; + public static final String FIELD_NODE_PATH = "nodePath"; + public static final String FIELD_NODE_NAME = "nodeName"; + public static final String FIELD_DUPLICATE_TITLE = "duplicate_new_title"; + public static final String FIELD_DUPLICATE_IDENTIFIER = "duplicate_view_identifier"; + public static final String FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS = "duplicate_reset_childItemIds"; + public static final String FIELD_USE_CUSTOM_VIEW = "use_custom_view"; + public static final String FIELD_CUSTOM_VIEW_SELECTOR = "custom_view_selector"; + public static final String FIELD_IS_AUTO_SELECT = "is_auto_select"; + public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; + public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; + + public static final String TRANS_SETTINGS_ID = "item_settings"; + public static final String TRANS_INIT_ID = "initialize"; + public static final String TRANS_SYNC_ID = "data_sync"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java new file mode 100644 index 00000000000..85816e809a3 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -0,0 +1,67 @@ +package com.netgrif.application.engine.menu.domain; + +import com.netgrif.application.engine.petrinet.domain.I18nString; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * todo javadoc + * */ +@Getter +public enum MenuItemView { + // todo translations + TABBED_CASE_VIEW(new I18nString("Tabbed case view"), "tabbed_case_view", + List.of("tabbed_task_view"), true), + TABBED_TASK_VIEW(new I18nString("Tabbed task view"), "tabbed_task_view", List.of(), true); + + private final I18nString name; + private final String identifier; + /** + * todo javadoc + * */ + private final List<String> allowedAssociatedViews; + private final boolean isTabbed; + + MenuItemView(I18nString name, String identifier, List<String> allowedAssociatedViews, boolean isTabbed) { + this.name = name; + this.identifier = identifier; + this.allowedAssociatedViews = allowedAssociatedViews; + this.isTabbed = isTabbed; + } + + /** + * todo javadoc + * */ + public static MenuItemView fromIdentifier(String identifier) { + for (MenuItemView view : MenuItemView.values()) { + if (view.identifier.equals(identifier)) { + return view; + } + } + throw new IllegalArgumentException(identifier); + } + + /** + * todo javadoc + * */ + public static List<MenuItemView> findAllByIsTabbed(boolean isTabbed) { + return Arrays.stream(MenuItemView.values()) + .filter(view -> view.isTabbed == isTabbed) + .collect(Collectors.toList()); + } + + /** + * todo javadoc + * */ + public static List<MenuItemView> findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { + MenuItemView parentView = fromIdentifier(parentIdentifier); + return Arrays.stream(MenuItemView.values()) + .filter(view -> view.isTabbed == isTabbed + && parentView.getAllowedAssociatedViews().contains(view.identifier)) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuList.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuList.java similarity index 95% rename from src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuList.java rename to src/main/java/com/netgrif/application/engine/menu/domain/MenuList.java index 9ee55560ed9..e76f0f7e40e 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuList.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuList.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.workflow.domain.menu; +package com.netgrif.application.engine.menu.domain; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java b/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java new file mode 100644 index 00000000000..183e1287ec4 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java @@ -0,0 +1,53 @@ +package com.netgrif.application.engine.menu.domain; + +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import lombok.AllArgsConstructor; +import lombok.Data; + +import javax.annotation.Nullable; +import java.util.*; + +@Data +@AllArgsConstructor +public class ToDataSetOutcome { + private Map<String, Map<String, Object>> dataSet; + /** + * todo javadoc + * */ + private ToDataSetOutcome associatedOutcome; + + public ToDataSetOutcome() { + this.dataSet = new HashMap<>(); + } + + public ToDataSetOutcome(Map<String, Map<String, Object>> dataSet) { + this.dataSet = dataSet; + } + + /** + * todo javadoc + * */ + public void putDataSetEntry(String fieldId, FieldType fieldType, @Nullable Object fieldValue) { + Map<String, Object> fieldMap = new LinkedHashMap<>(); + fieldMap.put("type", fieldType.getName()); + fieldMap.put("value", fieldValue); + this.dataSet.put(fieldId, fieldMap); + } + + /** + * todo javadoc + * */ + public void putDataSetEntryOptions(String fieldId, FieldType fieldType, @Nullable Map<String, I18nString> options) { + Map<String, Object> fieldMap = new LinkedHashMap<>(); + fieldMap.put("type", fieldType.getName()); + if (fieldType.equals(FieldType.MULTICHOICE_MAP)) { + fieldMap.put("value", Set.of()); + } + if (options == null) { + options = new HashMap<>(); + } + fieldMap.put("options", options); + this.dataSet.put(fieldId, fieldMap); + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java new file mode 100644 index 00000000000..ca2790c879b --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -0,0 +1,80 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TabbedCaseViewBody extends ViewBody { + private String viewSearchType = "fulltext_advanced"; + private String createCaseButtonTitle; + private String createCaseButtonIcon = "add"; + private boolean requireTitleInCreation = true; + private boolean showCreateCaseButton = true; + private String bannedNetsInCreation; + private boolean showMoreMenu = false; + private boolean allowHeaderTableMode = true; + private List<String> headersMode = new ArrayList<>(List.of("sort", "edit", "search")); + private String headersDefaultMode = "sort"; + private List<String> defaultHeaders; + private boolean isHeaderModeChangeable = true; + private boolean useDefaultHeaders = true; + + private ViewBody chainedView; + + @Override + public ViewBody getAssociatedViewBody() { + return this.chainedView; + } + + @Override + public MenuItemView getViewType() { + return MenuItemView.TABBED_CASE_VIEW; + } + + @Override + protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { + + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, + this.viewSearchType); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.TEXT, + this.createCaseButtonTitle); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, + this.createCaseButtonIcon); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, + this.requireTitleInCreation); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, + this.showCreateCaseButton); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, + this.bannedNetsInCreation); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, + this.showMoreMenu); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, + this.allowHeaderTableMode); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, + this.headersMode == null ? new ArrayList<>() : this.headersMode); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, + this.headersDefaultMode); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, + this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, + this.isHeaderModeChangeable); + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, + this.useDefaultHeaders); + if (this.chainedView != null) { + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, + this.chainedView.getViewType().getIdentifier()); + } + + return outcome; + } +} + diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java new file mode 100644 index 00000000000..3fa9d432c4b --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java @@ -0,0 +1,22 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +/** + * Here are declared constants of process tabbed_case_view_configuration.xml. + */ +public class TabbedCaseViewConstants extends ViewConstants { + public static final String FIELD_NEW_FILTER_ID = "new_filter_id"; + public static final String FIELD_DEFAULT_HEADERS = "default_headers"; + public static final String FIELD_REQUIRE_TITLE_IN_CREATION = "require_title_in_creation"; + public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; + public static final String FIELD_CREATE_CASE_BUTTON_TITLE = "create_case_button_title"; + public static final String FIELD_CREATE_CASE_BUTTON_ICON = "create_case_button_icon"; + public static final String FIELD_BANNED_NETS_IN_CREATION = "banned_nets_in_creation"; + public static final String FIELD_SHOW_CREATE_CASE_BUTTON = "show_create_case_button"; + public static final String FIELD_SHOW_MORE_MENU = "show_more_menu"; + public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "allow_header_table_mode"; + public static final String FIELD_HEADERS_MODE = "headers_mode"; + public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; + public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; + public static final String FIELD_USE_CASE_DEFAULT_HEADERS = "use_case_default_headers"; + public static final String FIELD_CONFIGURATION_TYPE = "view_configuration_type"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java new file mode 100644 index 00000000000..0fabe6940fe --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java @@ -0,0 +1,63 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import com.netgrif.application.engine.workflow.domain.Case; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TabbedTaskViewBody extends ViewBody { + private Case filter; + private boolean mergeFilters = true; + private String viewSearchType = "fulltext_advanced"; + private List<String> headersMode = new ArrayList<>(List.of("sort", "edit")); + private String headersDefaultMode = "sort"; + private boolean isHeaderModeChangeable = true; + private boolean allowHeaderTableMode = true; + private boolean useDefaultHeaders = true; + private List<String> defaultHeaders; + private boolean showMoreMenu = true; + + @Override + public ViewBody getAssociatedViewBody() { + return null; + } + + @Override + public MenuItemView getViewType() { + return MenuItemView.TABBED_TASK_VIEW; + } + + @Override + protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { + + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_MERGE_FILTERS, FieldType.BOOLEAN, + this.mergeFilters); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, + this.viewSearchType); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, + this.headersMode == null ? new ArrayList<>() : this.headersMode); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, + this.headersDefaultMode); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, + this.isHeaderModeChangeable); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, + this.allowHeaderTableMode); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, + this.useDefaultHeaders); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, + this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); + outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, + this.showMoreMenu); + + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java new file mode 100644 index 00000000000..f895558fa77 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java @@ -0,0 +1,16 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +/** + * Here are declared constants of process tabbed_task_view_configuration.xml. + */ +public class TabbedTaskViewConstants extends ViewConstants { + public static final String FIELD_MERGE_FILTERS = "merge_filters"; + public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; + public static final String FIELD_DEFAULT_HEADERS = "default_headers"; + public static final String FIELD_HEADERS_MODE = "headers_mode"; + public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; + public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; + public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "allow_header_table_mode"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; + public static final String FIELD_SHOW_MORE_MENU = "show_more_menu"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java new file mode 100644 index 00000000000..461f441f437 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -0,0 +1,70 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import com.netgrif.application.engine.workflow.domain.Case; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.annotation.Nullable; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public abstract class ViewBody { + + @Nullable + protected FilterBody filterBody; + + public abstract ViewBody getAssociatedViewBody(); + public abstract MenuItemView getViewType(); + /** + * todo javadoc + * */ + protected abstract ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome); + + /** + * todo javadoc + * */ + public boolean hasAssociatedView() { + return this.getAssociatedViewBody() != null; + } + + /** + * todo javadoc + * */ + public String getViewProcessIdentifier() { + return getViewType().getIdentifier() + "_configuration"; + } + + /** + * todo javadoc + * */ + public ToDataSetOutcome toDataSet() { + return toDataSet(null, null); + }; + + /** + * todo javadoc + * */ + public ToDataSetOutcome toDataSet(Case associatedViewCase, Case filterCase) { + ToDataSetOutcome outcome = new ToDataSetOutcome(); + + if (associatedViewCase != null) { + outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_ID, FieldType.CASE_REF, + List.of(associatedViewCase.getStringId())); + String taskId = MenuItemUtils.findTaskIdInCase(associatedViewCase, ViewConstants.TRANS_SETTINGS_ID); + outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_FORM, FieldType.TASK_REF, List.of(taskId)); + } + if (filterCase != null) { + outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_FILTER_CASE, FieldType.CASE_REF, List.of(filterCase.getStringId())); + } + + return toDataSetInternal(outcome); + }; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java new file mode 100644 index 00000000000..17482b8c2d0 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java @@ -0,0 +1,15 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +/** + * Here are declared general constants of menu item configuration processes. + */ +public class ViewConstants { + public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; + public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; + public static final String FIELD_VIEW_CONTAINS_FILTER = "contains_filter"; + public static final String FIELD_VIEW_FILTER_CASE = "filter_case"; + + public static final String TRANS_INIT_ID = "initialize"; + public static final String TRANS_SETTINGS_ID = "settings"; + public static final String TRANS_SYNC_ID = "data_sync"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java new file mode 100644 index 00000000000..46115a02bba --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -0,0 +1,545 @@ +package com.netgrif.application.engine.menu.services; + +import com.netgrif.application.engine.auth.domain.IUser; +import com.netgrif.application.engine.auth.domain.LoggedUser; +import com.netgrif.application.engine.auth.service.interfaces.IUserService; +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.MenuItemConstants; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.menu.domain.configurations.ViewBody; +import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; +import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.UriContentType; +import com.netgrif.application.engine.petrinet.domain.UriNode; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; +import com.netgrif.application.engine.startup.DefaultFiltersRunner; +import com.netgrif.application.engine.startup.FilterRunner; +import com.netgrif.application.engine.startup.ImportHelper; +import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.QCase; +import com.netgrif.application.engine.workflow.domain.Task; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; +import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; +import com.querydsl.core.types.Predicate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MenuItemService implements IMenuItemService { + // todo javadoc + protected final IWorkflowService workflowService; + protected final ITaskService taskService; + protected final IDataService dataService; + protected final IUserService userService; + protected final IUriService uriService; + + protected static final String DEFAULT_FOLDER_ICON = "folder"; + + @Override + public Case createFilter(FilterBody body) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + Case filterCase = createCase(FilterRunner.FILTER_PETRI_NET_IDENTIFIER, body.getTitle().getDefaultValue(), loggedUser.transformToLoggedUser()); + filterCase.setIcon(body.getIcon()); + filterCase = workflowService.save(filterCase); + ToDataSetOutcome dataSetOutcome = body.toDataSet(); + return setDataWithExecute(filterCase, DefaultFiltersRunner.AUTO_CREATE_TRANSITION, dataSetOutcome.getDataSet()); + } + + @Override + public Case updateFilter(Case filterCase, FilterBody body) { + filterCase.setIcon(body.getIcon()); + filterCase = workflowService.save(filterCase); + ToDataSetOutcome dataSetOutcome = body.toDataSet(); + return setData(filterCase, DefaultFiltersRunner.DETAILS_TRANSITION, dataSetOutcome.getDataSet()); + } + + @Override + public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + String sanitizedIdentifier = MenuItemUtils.sanitize(body.getIdentifier()); + + if (existsMenuItem(sanitizedIdentifier)) { + throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", sanitizedIdentifier)); + } + + Case parentItemCase = getOrCreateFolderItem(body.getUri()); + I18nString newName = body.getMenuName(); + if (newName == null) { + newName = new I18nString(body.getIdentifier()); + } + Case menuItemCase = createCase(FilterRunner.MENU_NET_IDENTIFIER, newName.getDefaultValue(), + loggedUser.transformToLoggedUser()); + menuItemCase.setUriNodeId(uriService.findByUri(body.getUri()).getStringId()); + menuItemCase = workflowService.save(menuItemCase); + + parentItemCase = appendChildCaseIdAndSave(parentItemCase, menuItemCase.getStringId()); + + String nodePath = createNodePath(body.getUri(), sanitizedIdentifier); + uriService.getOrCreate(nodePath, UriContentType.CASE); + + Case viewCase = null; + if (body.hasView()) { + viewCase = createView(body.getView()); + } + ToDataSetOutcome dataSetOutcome = body.toDataSet(parentItemCase.getStringId(), nodePath, viewCase); + return setDataWithExecute(menuItemCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + } + + @Override + public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNotExecutableException { + String actualUriNodeId = uriService.findByUri(body.getUri()).getStringId(); + if (!itemCase.getUriNodeId().equals(actualUriNodeId)) { + itemCase.setUriNodeId(actualUriNodeId); + itemCase = workflowService.save(itemCase); + } + + Case viewCase = findView(itemCase); + viewCase = handleView(viewCase, body.getView()); + ToDataSetOutcome dataSetOutcome = body.toDataSet(viewCase); + return setData(itemCase, MenuItemConstants.TRANS_SYNC_ID, dataSetOutcome.getDataSet()); + } + + @Override + public Case createOrUpdateMenuItem(MenuItemBody body) throws TransitionNotExecutableException { + Case itemCase = findMenuItem(MenuItemUtils.sanitize(body.getIdentifier())); + if (itemCase != null) { + return updateMenuItem(itemCase, body); + } else { + return createMenuItem(body); + } + } + + @Override + public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecutableException { + Case itemCase = findMenuItem(body.getIdentifier()); + if (itemCase != null) { + return itemCase; + } else { + return createMenuItem(body); + } + } + + @Override + public Case findMenuItem(String identifier) { + // return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.menu_item_identifier.textValue.keyword:\"$menuItemIdentifier\"" as String) + Predicate predicate = QCase.case$.processIdentifier.eq(FilterRunner.MENU_NET_IDENTIFIER) + .and(QCase.case$.dataSet.get("menu_item_identifier").value.eq(identifier)); + return workflowService.searchOne(predicate); + } + + @Override + public Case findMenuItem(String uri, String name) { + UriNode uriNode = uriService.findByUri(uri); +// return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND title.keyword:\"$name\" AND uriNodeId:\"$uriNode.stringId\"") + Predicate predicate = QCase.case$.processIdentifier.eq(FilterRunner.MENU_NET_IDENTIFIER) + .and(QCase.case$.title.eq(name)) + .and(QCase.case$.uriNodeId.eq(uriNode.getStringId())); + return workflowService.searchOne(predicate); + } + + @Override + public Case findFolderCase(UriNode node) { + // todo elastic problem +// return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$node.uriPath\"") + Predicate predicate = QCase.case$.processIdentifier.eq(FilterRunner.MENU_NET_IDENTIFIER) + .and(QCase.case$.dataSet.get("nodePath").value.eq(node.getUriPath())); + return workflowService.searchOne(predicate); + } + + @Override + public boolean existsMenuItem(String identifier) { + // return countCasesElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.menu_item_identifier.fulltextValue.keyword:\"$menuItemIdentifier\"") > 0 + return findMenuItem(identifier) != null; + } + + @Override + public void moveItem(Case itemCase, String destUri) throws TransitionNotExecutableException { + if (MenuItemUtils.isCyclicNodePath(itemCase, destUri)) { + throw new IllegalArgumentException(String.format("Cyclic path not supported. Destination path: %s", destUri)); + } + List<Case> casesToSave = new ArrayList<>(); + + List<String> parentIdList = MenuItemUtils.getCaseIdsFromCaseRef(itemCase, MenuItemConstants.FIELD_PARENT_ID); + if (parentIdList != null && !parentIdList.isEmpty()) { + Case oldParent = removeChildItemFromParent(parentIdList.get(0), itemCase); + casesToSave.add(oldParent); + } + + UriNode destNode = uriService.getOrCreate(destUri, UriContentType.CASE); + Case newParent = getOrCreateFolderItem(destNode.getUriPath()); + if (newParent != null) { + itemCase.getDataField(MenuItemConstants.FIELD_PARENT_ID).setValue(List.of(newParent.getStringId())); + appendChildCaseIdInMemory(newParent, itemCase.getStringId()); + casesToSave.add(newParent); + } else { + itemCase.getDataField(MenuItemConstants.FIELD_PARENT_ID).setValue(null); + } + + itemCase.setUriNodeId(destNode.getStringId()); + resolveAndHandleNewNodePath(itemCase, destNode.getUriPath()); + casesToSave.add(itemCase); + + if (MenuItemUtils.hasFolderChildren(itemCase)) { + List<Case> childrenToSave = updateNodeInChildrenFoldersRecursive(itemCase); + casesToSave.addAll(childrenToSave); + } + + for (Case useCase : casesToSave) { + if (useCase != null) { + workflowService.save(useCase); + } + } + } + + @Override + public Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException { + if (newIdentifier == null || newIdentifier.isEmpty()) { + throw new IllegalArgumentException("View item identifier is null or empty!"); + } + if (newTitle == null || newTitle.getDefaultValue().isEmpty()) { + throw new IllegalArgumentException("Default title is null or empty"); + } + String sanitizedIdentifier = MenuItemUtils.sanitize(newIdentifier); + if (existsMenuItem(sanitizedIdentifier)) { + throw new IllegalArgumentException(String.format("View item identifier %s is not unique!", sanitizedIdentifier)); + } + + Case duplicatedViewCase = null; + if (MenuItemUtils.hasView(originItem)) { + String originViewId = MenuItemUtils.getCaseIdFromCaseRef(originItem, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID); + Case originViewCase = workflowService.findOne(originViewId); + duplicatedViewCase = duplicateView(originViewCase); + } + + Case duplicated = createCase(FilterRunner.MENU_NET_IDENTIFIER, newTitle.getDefaultValue(), + userService.getLoggedOrSystem().transformToLoggedUser()); + duplicated.setUriNodeId(originItem.getUriNodeId()); + duplicated.setDataSet(originItem.getDataSet()); + duplicated.setTitle(newTitle.getDefaultValue()); + duplicated = workflowService.save(duplicated); + + UriNode node = uriService.findById(originItem.getUriNodeId()); + String newNodePath = createNodePath(node.getUriPath(), sanitizedIdentifier); + uriService.getOrCreate(newNodePath, UriContentType.CASE); + + Map<String, Map<String, Object>> dataSet = new HashMap<>(); + dataSet.put(MenuItemConstants.FIELD_DUPLICATE_TITLE, Map.of("type", FieldType.I18N.getName(), "value", + new I18nString(""))); + dataSet.put(MenuItemConstants.FIELD_DUPLICATE_IDENTIFIER, Map.of("type", FieldType.TEXT.getName(), + "value","")); + dataSet.put(MenuItemConstants.FIELD_MENU_NAME, Map.of("type", FieldType.I18N.getName(), + "value", newTitle)); + dataSet.put(MenuItemConstants.FIELD_TAB_NAME, Map.of("type", FieldType.I18N.getName(), + "value", newTitle)); + dataSet.put(MenuItemConstants.FIELD_NODE_PATH, Map.of("type", FieldType.TEXT.getName(), + "value", newNodePath)); + // Must be reset by button, because we have the same dataSet reference between originItem and duplicated + dataSet.put(MenuItemConstants.FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS, Map.of("type", FieldType.BUTTON.getName(), + "value", 0)); + if (duplicatedViewCase != null) { + addConfigurationIntoDataSet(duplicatedViewCase, dataSet); + } + + setDataWithExecute(duplicated, MenuItemConstants.TRANS_INIT_ID, dataSet); + + List<String> parentIdAsList = MenuItemUtils.getCaseIdsFromCaseRef(originItem, MenuItemConstants.FIELD_PARENT_ID); + if (parentIdAsList != null && !parentIdAsList.isEmpty()) { + Case parent = workflowService.findOne(parentIdAsList.get(0)); + appendChildCaseIdAndSave(parent, duplicated.getStringId()); + } + return workflowService.findOne(duplicated.getStringId()); + } + + @Override + public Case removeChildItemFromParent(String folderId, Case childItem) { + Case parentFolder = workflowService.findOne(folderId); + List<String> childIds = MenuItemUtils.getCaseIdsFromCaseRef(parentFolder, MenuItemConstants.FIELD_CHILD_ITEM_IDS); + if (childIds == null || childIds.isEmpty()) { + return parentFolder; + } + childIds.remove(childItem.getStringId()); + parentFolder.getDataField(MenuItemConstants.FIELD_CHILD_ITEM_IDS).setValue(childIds); + parentFolder.getDataField(MenuItemConstants.FIELD_HAS_CHILDREN).setValue(MenuItemUtils.hasFolderChildren(parentFolder)); + return workflowService.save(parentFolder); + } + + protected Case duplicateView(Case viewCase) throws TransitionNotExecutableException { + Case duplicatedAssociatedViewCase = null; + if (MenuItemUtils.hasView(viewCase)) { + String originViewId = MenuItemUtils.getCaseIdFromCaseRef(viewCase, ViewConstants.FIELD_VIEW_CONFIGURATION_ID); + Case originViewCase = workflowService.findOne(originViewId); + duplicatedAssociatedViewCase = duplicateView(originViewCase); + } + + Case duplicatedViewCase = createCase(viewCase.getProcessIdentifier(), viewCase.getTitle(), + userService.getLoggedOrSystem().transformToLoggedUser()); + duplicatedViewCase.setDataSet(viewCase.getDataSet()); + workflowService.save(duplicatedViewCase); + + Map<String, Map<String, Object>> dataSet = new HashMap<>(); + if (duplicatedAssociatedViewCase != null) { + addConfigurationIntoDataSet(duplicatedAssociatedViewCase, dataSet); + } + + return setDataWithExecute(duplicatedViewCase, MenuItemConstants.TRANS_INIT_ID, dataSet); + } + + protected Case findView(Case itemOrViewCase) { + return findCaseInCaseRef(itemOrViewCase, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID); + } + + protected Case findFilter(Case viewCase) { + return findCaseInCaseRef(viewCase, ViewConstants.FIELD_VIEW_FILTER_CASE); + } + + protected Case findCaseInCaseRef(Case useCase, String caseRefId) { + try { + String caseId = MenuItemUtils.getCaseIdFromCaseRef(useCase, caseRefId); + return workflowService.findOne(caseId); + } catch (IllegalArgumentException | NullPointerException ignore) { + return null; + } + } + + protected Case handleView(Case existingViewCase, ViewBody body) throws TransitionNotExecutableException { + if (mustUpdateView(existingViewCase, body)) { + return updateView(existingViewCase, body); + } else if (mustCreateView(existingViewCase, body)) { + return createView(body); + } else if (mustRemoveView(existingViewCase, body)) { + removeView(existingViewCase); + return null; + } else if (mustRemoveAndCreateView(existingViewCase, body)) { + removeView(existingViewCase); + return createView(body); + } else { + return null; + } + } + + protected Case createView(ViewBody body) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + Case viewCase = createCase(body.getViewProcessIdentifier(), body.getViewProcessIdentifier(), + loggedUser.transformToLoggedUser()); + + Case associatedViewCase = null; + if (body.hasAssociatedView()) { + associatedViewCase = createView(body.getAssociatedViewBody()); + } + Case filterCase = null; + if (body.getFilterBody() != null) { + if (body.getFilterBody().getFilter() != null) { + filterCase = body.getFilterBody().getFilter(); + } else { + filterCase = createFilter(body.getFilterBody()); + } + } + ToDataSetOutcome dataSetOutcome = body.toDataSet(associatedViewCase, filterCase); + return setDataWithExecute(viewCase, ViewConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + } + + protected Case updateView(Case viewCase, ViewBody body) throws TransitionNotExecutableException { + Case filterCase = findFilter(viewCase); + filterCase = handleFilter(filterCase, body.getFilterBody()); + + Case associatedViewCase = findView(viewCase); + associatedViewCase = handleView(associatedViewCase, body.getAssociatedViewBody()); + + ToDataSetOutcome outcome = body.toDataSet(associatedViewCase, filterCase); + return setData(viewCase, ViewConstants.TRANS_SYNC_ID, outcome.getDataSet()); + } + + protected void removeView(Case viewCase) { + workflowService.deleteCase(viewCase); + } + + protected Case handleFilter(Case filterCase, FilterBody body) throws TransitionNotExecutableException { + if (mustCreateFilter(filterCase, body)) { + return createFilter(body); + } else if (mustUpdateFilter(filterCase, body)){ + return updateFilter(filterCase, body); + } else { + return filterCase; + } + } + + protected boolean mustUpdateView(Case useCase, ViewBody body) { + return body != null && useCase != null && useCase.getProcessIdentifier().equals(body.getViewProcessIdentifier()); + } + + protected boolean mustRemoveAndCreateView(Case useCase, ViewBody body) { + return body != null && useCase != null && !useCase.getProcessIdentifier().equals(body.getViewProcessIdentifier()); + } + + protected boolean mustRemoveView(Case useCase, ViewBody body) { + return body == null && useCase != null; + } + + protected boolean mustCreateView(Case useCase, ViewBody body) { + return body != null && useCase == null; + } + + protected boolean mustCreateFilter(Case filterCase, FilterBody body) { + return filterCase == null && body != null; + } + + protected boolean mustUpdateFilter(Case filterCase, FilterBody body) { + return filterCase != null && body != null; + } + + protected List<Case> updateNodeInChildrenFoldersRecursive(Case parentFolder) { + List<String> childItemIds = MenuItemUtils.getCaseIdsFromCaseRef(parentFolder, MenuItemConstants.FIELD_CHILD_ITEM_IDS); + if (childItemIds == null || childItemIds.isEmpty()) { + return new ArrayList<>(); + } + + List<Case> children = workflowService.findAllById(childItemIds); + + List<Case> casesToSave = new ArrayList<>(); + for (Case childCase : children) { + UriNode parentNode = uriService.getOrCreate((String) parentFolder.getFieldValue(MenuItemConstants.FIELD_NODE_PATH), + UriContentType.CASE); + childCase.setUriNodeId(parentNode.getStringId()); + resolveAndHandleNewNodePath(childCase, parentNode.getUriPath()); + + casesToSave.add(childCase); + casesToSave.addAll(updateNodeInChildrenFoldersRecursive(childCase)); + } + + return casesToSave; + } + + protected void resolveAndHandleNewNodePath(Case folderItem, String destUri) { + String newNodePath = resolveNewNodePath(folderItem, destUri); + UriNode newNode = uriService.getOrCreate(newNodePath, UriContentType.CASE); + folderItem.getDataField(MenuItemConstants.FIELD_NODE_PATH).setValue(newNode.getUriPath()); + } + + protected String resolveNewNodePath(Case folderItem, String destUri) { + return destUri + uriService.getUriSeparator() + folderItem.getFieldValue(MenuItemConstants.FIELD_IDENTIFIER); + } + + protected String createNodePath(String uri, String identifier) { + if (Objects.equals(uri, uriService.getUriSeparator())) { + return uri + identifier; + } else { + return uri + uriService.getUriSeparator() + identifier; + } + } + + protected Case getOrCreateFolderItem(String uri) throws TransitionNotExecutableException { + UriNode node = uriService.getOrCreate(uri, UriContentType.CASE); + MenuItemBody body = new MenuItemBody(new I18nString(node.getName()), DEFAULT_FOLDER_ICON); + return getOrCreateFolderRecursive(node, body); + } + + protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body) throws TransitionNotExecutableException { + return getOrCreateFolderRecursive(node, body, null); + } + protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case childFolderCase) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + Case folderCase = findFolderCase(node); + if (folderCase != null) { + if (childFolderCase != null) { + appendChildCaseIdAndSave(folderCase, childFolderCase.getStringId()); + } + return folderCase; + } + + folderCase = createCase(FilterRunner.MENU_NET_IDENTIFIER, body.getMenuName().getDefaultValue(), + loggedUser.transformToLoggedUser()); + folderCase.setUriNodeId(node.getParentId()); + folderCase = workflowService.save(folderCase); + + ToDataSetOutcome dataSetOutcome = body.toDataSet(null, node.getUriPath(), null); + if (childFolderCase != null) { + appendChildCaseIdInDataSet(folderCase, childFolderCase.getStringId(), dataSetOutcome.getDataSet()); + } + + if (node.getParentId() != null) { + UriNode parentNode = uriService.findById(node.getParentId()); + body = new MenuItemBody(new I18nString(parentNode.getName()), DEFAULT_FOLDER_ICON); + + Case parentFolderCase = getOrCreateFolderRecursive(parentNode, body, folderCase); + dataSetOutcome.putDataSetEntry(MenuItemConstants.FIELD_PARENT_ID, FieldType.CASE_REF, List.of(parentFolderCase.getStringId())); + } + folderCase = setDataWithExecute(folderCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + + return folderCase; + } + + protected void appendChildCaseIdInDataSet(Case folderCase, String childItemCaseId, Map<String, Map<String, Object>> dataSet) { + List<String> childIds = MenuItemUtils.getCaseIdsFromCaseRef(folderCase, MenuItemConstants.FIELD_CHILD_ITEM_IDS); + if (childIds == null || childIds.isEmpty()) { + dataSet.put(MenuItemConstants.FIELD_CHILD_ITEM_IDS, Map.of("type", FieldType.CASE_REF.getName(), + "value", List.of(childItemCaseId))); + } else { + childIds.add(childItemCaseId); + dataSet.put(MenuItemConstants.FIELD_CHILD_ITEM_IDS, Map.of("type", FieldType.CASE_REF.getName(), + "value", childIds)); + } + dataSet.put(MenuItemConstants.FIELD_HAS_CHILDREN, Map.of("type", FieldType.BOOLEAN.getName(), + "value", MenuItemUtils.hasFolderChildren(folderCase))); + } + + protected void appendChildCaseIdInMemory(Case folderCase, String childItemCaseId) { + List<String> childIds = MenuItemUtils.getCaseIdsFromCaseRef(folderCase, MenuItemConstants.FIELD_CHILD_ITEM_IDS); + if (childIds == null || childIds.isEmpty()) { + folderCase.getDataField(MenuItemConstants.FIELD_CHILD_ITEM_IDS).setValue(List.of(childItemCaseId)); + } else { + childIds.add(childItemCaseId); + folderCase.getDataField(MenuItemConstants.FIELD_CHILD_ITEM_IDS).setValue(childIds); + } + folderCase.getDataField(MenuItemConstants.FIELD_HAS_CHILDREN).setValue(MenuItemUtils.hasFolderChildren(folderCase)); + } + + protected Case appendChildCaseIdAndSave(Case folderCase, String childItemCaseId) { + Map<String, Map<String, Object>> dataSet = new HashMap<>(); + appendChildCaseIdInDataSet(folderCase, childItemCaseId, dataSet); + return setData(folderCase, MenuItemConstants.TRANS_SYNC_ID, dataSet); + } + + protected void addConfigurationIntoDataSet(Case configurationCase, Map<String, Map<String, Object>> dataSet) { + dataSet.put(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID, Map.of("type", FieldType.CASE_REF.getName(), + "value", List.of(configurationCase.getStringId()))); + String taskId = MenuItemUtils.findTaskIdInCase(configurationCase, ViewConstants.TRANS_SETTINGS_ID); + dataSet.put(MenuItemConstants.FIELD_VIEW_CONFIGURATION_FORM, Map.of("type", FieldType.TASK_REF.getName(), + "value", List.of(taskId))); + } + + protected Case createCase(String identifier, String title, LoggedUser loggedUser) { + return workflowService.createCaseByIdentifier(identifier, title, "",loggedUser).getCase(); + } + + protected Case setData(Case useCase, String transId, Map<String, Map<String, Object>> dataSet) { + String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); + return setData(taskId, dataSet); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected Case setData(String taskId, Map<String, Map<String, Object>> dataSet) { + return dataService.setData(taskId, ImportHelper.populateDataset((Map) dataSet)).getCase(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected Case setDataWithExecute(Case useCase, String transId, Map<String, Map<String, Object>> dataSet) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); + Task task = taskService.findOne(taskId); + task = taskService.assignTask(task, loggedUser).getTask(); + task = dataService.setData(task, ImportHelper.populateDataset((Map) dataSet)).getTask(); + return taskService.finishTask(task, loggedUser).getCase(); + } + +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java new file mode 100644 index 00000000000..8ffba28cf29 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java @@ -0,0 +1,50 @@ +package com.netgrif.application.engine.menu.services.interfaces; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.UriNode; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.workflow.domain.Case; + +import java.util.Map; +import java.util.stream.Collectors; + +public interface IMenuItemService { + + Case createFilter(FilterBody body) throws TransitionNotExecutableException; + Case updateFilter(Case filterCase, FilterBody body); + Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableException; + Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNotExecutableException; + Case createOrUpdateMenuItem(MenuItemBody body) throws TransitionNotExecutableException; + Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecutableException; + Case findMenuItem(String identifier); + Case findMenuItem(String uri, String name); + Case findFolderCase(UriNode node); + boolean existsMenuItem(String identifier); + void moveItem(Case item, String destUri) throws TransitionNotExecutableException; + Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; + Case removeChildItemFromParent(String folderId, Case childItem); + + /** + * todo javadoc + * */ + default Map<String, I18nString> getAvailableViewsAsOptions(boolean isTabbed) { + return MenuItemView.findAllByIsTabbed(isTabbed).stream() + .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); + } + + /** + * todo javadoc + * */ + default Map<String, I18nString> getAvailableViewsAsOptions(boolean isTabbed, String viewIdentifier) { + int index = viewIdentifier.lastIndexOf("_configuration"); + if (index > 0) { + viewIdentifier = viewIdentifier.substring(0, index); + } + return MenuItemView.findAllByIsTabbedAndParentIdentifier(isTabbed, viewIdentifier).stream() + .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); + } + +} diff --git a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java new file mode 100644 index 00000000000..e486fa18c25 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java @@ -0,0 +1,91 @@ +package com.netgrif.application.engine.menu.utils; + +import com.netgrif.application.engine.menu.domain.MenuItemConstants; +import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.TaskPair; + +import java.text.Normalizer; +import java.util.List; + +public class MenuItemUtils { + + /** + * todo javadoc + * */ + public static String sanitize(String input) { + if (input == null) { + return null; + } + return Normalizer.normalize(input.trim(), Normalizer.Form.NFD) + .replaceAll("[^\\p{ASCII}]", "") + .replaceAll("\\p{InCombiningDiacriticalMarks}+", "") + .replaceAll("[\\W-]+", "-") + .toLowerCase(); + } + + /** + * todo javadoc + */ + public static String findTaskIdInCase(Case useCase, String transId) { + if (useCase == null || transId == null) { + return null; + } + + TaskPair resultPair = useCase.getTasks().stream() + .filter(taskPair -> taskPair.getTransition().equals(transId)) + .findFirst().orElse(null); + + if (resultPair == null) { + return null; + } + + return resultPair.getTask(); + } + + /** + * todo javadoc + * */ + public static boolean isCyclicNodePath(Case folderItem, String destUri) { + String oldNodePath = (String) folderItem.getFieldValue(MenuItemConstants.FIELD_NODE_PATH); + return destUri.contains(oldNodePath); + } + + /** + * todo javadoc + * */ + @SuppressWarnings("unchecked") + public static List<String> getCaseIdsFromCaseRef(Case useCase, String caseRefId) { + try { + return (List<String>) useCase.getFieldValue(caseRefId); + } catch (NullPointerException ignore) { + return null; + } + } + + /** + * todo javadoc + * */ + public static String getCaseIdFromCaseRef(Case useCase, String caseRefId) { + List<String> caseIds = getCaseIdsFromCaseRef(useCase, caseRefId); + if (caseIds == null || caseIds.isEmpty()) { + return null; + } + return caseIds.get(0); + } + + /** + * todo javadoc + * */ + public static boolean hasView(Case menuItemCase) { + return getCaseIdFromCaseRef(menuItemCase, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) != null; + } + + /** + * todo javadoc + * */ + public static boolean hasFolderChildren(Case folderCase) { + List<String> childIds = MenuItemUtils.getCaseIdsFromCaseRef(folderCase, MenuItemConstants.FIELD_CHILD_ITEM_IDS); + return childIds != null && !childIds.isEmpty(); + } + +} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java deleted file mode 100644 index cdb09cdbf29..00000000000 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.netgrif.application.engine.workflow.domain.menu; - -import com.netgrif.application.engine.petrinet.domain.I18nString; -import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; -import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate; -import com.netgrif.application.engine.workflow.domain.Case; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.annotation.Nullable; -import java.text.Normalizer; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * Class, that holds configurable attributes of menu item. In case of attribute addition, please update also - * {@link MenuItemBody#toDataSet(String, String, boolean)} method. - */ -@Data -@NoArgsConstructor -public class MenuItemBody { - - // generic attributes - private I18nString menuName; - private I18nString tabName; - private String menuIcon = "filter_none"; - private String tabIcon; - private String uri; - private String identifier; - private Case filter; - private Map<String, I18nString> allowedRoles; - private Map<String, I18nString> bannedRoles; - private boolean useTabIcon = true; - private boolean useCustomView = false; - private String customViewSelector; - private boolean isAutoSelect = false; - - // case view attributes - private String caseViewSearchType = "fulltext_advanced"; - private String createCaseButtonTitle; - private String createCaseButtonIcon = "add"; - private boolean caseRequireTitleInCreation = true; - private boolean showCreateCaseButton = true; - private String bannedNetsInCreation; - private boolean caseShowMoreMenu = false; - private boolean caseAllowHeaderTableMode = true; - private List<String> caseHeadersMode = new ArrayList<>(List.of("sort", "edit", "search")); - private String caseHeadersDefaultMode = "sort"; - private List<String> caseDefaultHeaders; - private boolean caseIsHeaderModeChangeable = true; - private boolean caseUseDefaultHeaders = true; - - // task view attributes - private Case additionalFilter; - private boolean mergeFilters = true; - private String taskViewSearchType = "fulltext_advanced"; - private List<String> taskHeadersMode = new ArrayList<>(List.of("sort", "edit")); - private String taskHeadersDefaultMode = "sort"; - private boolean taskIsHeaderModeChangeable = true; - private boolean taskAllowHeaderTableMode = true; - private boolean taskUseDefaultHeaders = true; - private List<String> taskDefaultHeaders; - private boolean taskShowMoreMenu = true; - - public MenuItemBody(I18nString name, String icon) { - this.menuName = name; - this.tabName = name; - this.menuIcon = icon; - this.tabIcon = icon; - } - - public MenuItemBody(I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { - this.menuName = menuName; - this.tabName = tabName; - this.menuIcon = menuIcon; - this.tabIcon = tabIcon; - } - - public MenuItemBody(String uri, String identifier, I18nString name, String icon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = name; - this.tabName = name; - this.menuIcon = icon; - this.tabIcon = icon; - } - - public MenuItemBody(String uri, String identifier, I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = menuName; - this.tabName = tabName; - this.menuIcon = menuIcon; - this.tabIcon = tabIcon; - } - - public MenuItemBody(String uri, String identifier, String name, String icon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = new I18nString(name); - this.tabName = new I18nString(name); - this.menuIcon = icon; - this.tabIcon = icon; - } - - public MenuItemBody(String uri, String identifier, String menuName, String tabName, String menuIcon, String tabIcon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = new I18nString(menuName); - this.tabName = new I18nString(tabName); - this.menuIcon = menuIcon; - this.tabIcon = tabIcon; - } - - private static void putDataSetEntry(Map<String, Map<String, Object>> dataSet, MenuItemConstants fieldId, FieldType fieldType, - @Nullable Object fieldValue) { - Map<String, Object> fieldMap = new LinkedHashMap<>(); - fieldMap.put("type", fieldType.getName()); - fieldMap.put("value", fieldValue); - dataSet.put(fieldId.getAttributeId(), fieldMap); - } - - private static String sanitize(String input) { - if (input == null) { - return null; - } - return Normalizer.normalize(input.trim(), Normalizer.Form.NFD) - .replaceAll("[^\\p{ASCII}]", "") - .replaceAll("\\p{InCombiningDiacriticalMarks}+", "") - .replaceAll("[\\W-]+", "-") - .toLowerCase(); - } - - public String getIdentifier() { - return sanitize(this.identifier); - } - - public void setMenuName(I18nString name) { - this.menuName = name; - } - - public void setMenuName(String name) { - this.menuName = new I18nString(name); - } - - public void setTabName(I18nString name) { - this.tabName = name; - } - - public void setTabName(String name) { - this.tabName = new I18nString(name); - } - - /** - * Transforms attributes into dataSet for {@link ActionDelegate#setData} - * - * @return created dataSet from attributes - */ - public Map<String, Map<String, Object>> toDataSet() { - return toDataSet(null, null, true); - } - - /** - * Transforms attributes into dataSet for {@link ActionDelegate#setData} - * - * @param parentId id of parent menu item instance - * @param nodePath uri, that represents the menu item (f.e.: "/myItem1/myItem2") - * @return created dataSet from attributes - */ - public Map<String, Map<String, Object>> toDataSet(String parentId, String nodePath) { - return toDataSet(parentId, nodePath, false); - } - - private Map<String, Map<String, Object>> toDataSet(String parentId, String nodePath, boolean ignoreParentId) { - Map<String, Map<String, Object>> dataSet = new LinkedHashMap<>(); - - // GENERIC - ArrayList<String> filterIdCaseRefValue = new ArrayList<>(); - if (this.filter != null) { - filterIdCaseRefValue.add(this.filter.getStringId()); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CONTAINS_FILTER, FieldType.BOOLEAN, true); - } - ArrayList<String> parentIdCaseRef = new ArrayList<>(); - if (parentId != null) { - parentIdCaseRef.add(parentId); - } - - if (nodePath != null) { - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH, FieldType.TEXT, nodePath); - } - if (!ignoreParentId) { - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID, FieldType.CASE_REF, parentIdCaseRef); - } - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME, FieldType.I18N, this.menuName); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON, FieldType.TEXT, this.menuIcon); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_NAME, FieldType.I18N, this.tabName); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_ICON, FieldType.TEXT, this.tabIcon); - if (this.identifier != null) { - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER, FieldType.TEXT, this.getIdentifier()); - } - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_FILTER_CASE, FieldType.CASE_REF, filterIdCaseRefValue); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_TAB_ICON, FieldType.BOOLEAN, this.useTabIcon); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW, FieldType.BOOLEAN, - this.useCustomView); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR, FieldType.TEXT, - this.customViewSelector); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_IS_AUTO_SELECT, FieldType.BOOLEAN, this.isAutoSelect); - - // CASE - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, - this.caseViewSearchType); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.TEXT, - this.createCaseButtonTitle); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, - this.createCaseButtonIcon); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, - this.caseRequireTitleInCreation); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, - this.showCreateCaseButton); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, - this.bannedNetsInCreation); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_SHOW_MORE_MENU, FieldType.BOOLEAN, - this.caseShowMoreMenu); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, - this.caseAllowHeaderTableMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_HEADERS_MODE, FieldType.MULTICHOICE_MAP, - this.caseHeadersMode == null ? new ArrayList<>() : this.caseHeadersMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, - this.caseHeadersDefaultMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS, FieldType.TEXT, - this.caseDefaultHeaders != null ? String.join(",", this.caseDefaultHeaders) : null); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, - this.caseIsHeaderModeChangeable); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, - this.caseUseDefaultHeaders); - - // TASK - ArrayList<String> additionalFilterIdCaseRefValue = new ArrayList<>(); - if (this.additionalFilter != null) { - additionalFilterIdCaseRefValue.add(this.additionalFilter.getStringId()); - } - - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_ADDITIONAL_FILTER_CASE, FieldType.CASE_REF, - additionalFilterIdCaseRefValue); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_MERGE_FILTERS, FieldType.BOOLEAN, - this.mergeFilters); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, - this.taskViewSearchType); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_HEADERS_MODE, FieldType.MULTICHOICE_MAP, - this.taskHeadersMode == null ? new ArrayList<>() : this.taskHeadersMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, - this.taskHeadersDefaultMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, - this.taskIsHeaderModeChangeable); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, - this.taskAllowHeaderTableMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_TASK_DEFAULT_HEADERS, FieldType.BOOLEAN, - this.taskUseDefaultHeaders); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS, FieldType.TEXT, - this.taskDefaultHeaders != null ? String.join(",", this.taskDefaultHeaders) : null); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_SHOW_MORE_MENU, FieldType.BOOLEAN, - this.taskShowMoreMenu); - - return dataSet; - } -} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java deleted file mode 100644 index 8d9ab8a9190..00000000000 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.netgrif.application.engine.workflow.domain.menu; - -import lombok.Getter; - -/** - * Enumeration for menu items. It contains any constants needed in application. - */ -public enum MenuItemConstants { - - // FIELDS - PREFERENCE_ITEM_FIELD_NEW_FILTER_ID("new_filter_id"), - PREFERENCE_ITEM_FIELD_FILTER_CASE("filter_case"), - PREFERENCE_ITEM_FIELD_CONTAINS_FILTER("contains_filter"), - PREFERENCE_ITEM_FIELD_PARENT_ID("parentId"), - PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS("childItemIds"), - PREFERENCE_ITEM_FIELD_HAS_CHILDREN("hasChildren"), - PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS("case_default_headers"), - PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS("task_default_headers"), - PREFERENCE_ITEM_FIELD_IDENTIFIER("menu_item_identifier"), - PREFERENCE_ITEM_FIELD_APPEND_MENU_ITEM("append_menu_item_stringId"), - PREFERENCE_ITEM_FIELD_ALLOWED_ROLES("allowed_roles"), - PREFERENCE_ITEM_FIELD_BANNED_ROLES("banned_roles"), - PREFERENCE_ITEM_FIELD_MENU_NAME("menu_name"), - PREFERENCE_ITEM_FIELD_MENU_ICON("menu_icon"), - PREFERENCE_ITEM_FIELD_TAB_NAME("tab_name"), - PREFERENCE_ITEM_FIELD_USE_TAB_ICON("use_tab_icon"), - PREFERENCE_ITEM_FIELD_TAB_ICON("tab_icon"), - PREFERENCE_ITEM_FIELD_NODE_PATH("nodePath"), - PREFERENCE_ITEM_FIELD_NODE_NAME("nodeName"), - PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE("duplicate_new_title"), - PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER("duplicate_view_identifier"), - PREFERENCE_ITEM_FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS("duplicate_reset_childItemIds"), - PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION("case_require_title_in_creation"), - PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW("use_custom_view"), - PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR("custom_view_selector"), - PREFERENCE_ITEM_FIELD_CASE_VIEW_SEARCH_TYPE("case_view_search_type"), - PREFERENCE_ITEM_FIELD_IS_AUTO_SELECT("is_auto_select"), - PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_TITLE("create_case_button_title"), - PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_ICON("create_case_button_icon"), - PREFERENCE_ITEM_FIELD_BANNED_NETS_IN_CREATION("case_banned_nets_in_creation"), - PREFERENCE_ITEM_FIELD_SHOW_CREATE_CASE_BUTTON("show_create_case_button"), - PREFERENCE_ITEM_FIELD_CASE_SHOW_MORE_MENU("case_show_more_menu"), - PREFERENCE_ITEM_FIELD_CASE_ALLOW_HEADER_TABLE_MODE("case_allow_header_table_mode"), - PREFERENCE_ITEM_FIELD_CASE_HEADERS_MODE("case_headers_mode"), - PREFERENCE_ITEM_FIELD_CASE_HEADERS_DEFAULT_MODE("case_headers_default_mode"), - PREFERENCE_ITEM_FIELD_CASE_IS_HEADER_MODE_CHANGEABLE("case_is_header_mode_changeable"), - PREFERENCE_ITEM_FIELD_USE_CASE_DEFAULT_HEADERS("case_is_header_mode_changeable"), - PREFERENCE_ITEM_FIELD_ADDITIONAL_FILTER_CASE("additional_filter_case"), - PREFERENCE_ITEM_FIELD_MERGE_FILTERS("merge_filters"), - PREFERENCE_ITEM_FIELD_TASK_VIEW_SEARCH_TYPE("task_view_search_type"), - PREFERENCE_ITEM_FIELD_TASK_HEADERS_MODE("task_headers_mode"), - PREFERENCE_ITEM_FIELD_TASK_HEADERS_DEFAULT_MODE("task_headers_default_mode"), - PREFERENCE_ITEM_FIELD_TASK_IS_HEADER_MODE_CHANGEABLE("task_is_header_mode_changeable"), - PREFERENCE_ITEM_FIELD_TASK_ALLOW_HEADER_TABLE_MODE("task_allow_header_table_mode"), - PREFERENCE_ITEM_FIELD_USE_TASK_DEFAULT_HEADERS("use_task_default_headers"), - PREFERENCE_ITEM_FIELD_TASK_SHOW_MORE_MENU("task_show_more_menu"), - - // TRANSITIONS - PREFERENCE_ITEM_SETTINGS_TRANS_ID("item_settings"), - PREFERENCE_ITEM_FIELD_INIT_TRANS_ID("initialize"); - @Getter - private final String attributeId; - - MenuItemConstants(String attributeId) { - this.attributeId = attributeId; - } -} diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java b/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java index edb99e46623..68af8b19387 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java +++ b/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java @@ -20,10 +20,10 @@ import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.utils.InputStreamToString; import com.netgrif.application.engine.workflow.domain.*; -import com.netgrif.application.engine.workflow.domain.menu.Menu; -import com.netgrif.application.engine.workflow.domain.menu.MenuAndFilters; -import com.netgrif.application.engine.workflow.domain.menu.MenuEntry; -import com.netgrif.application.engine.workflow.domain.menu.MenuEntryRole; +import com.netgrif.application.engine.menu.domain.Menu; +import com.netgrif.application.engine.menu.domain.MenuAndFilters; +import com.netgrif.application.engine.menu.domain.MenuEntry; +import com.netgrif.application.engine.menu.domain.MenuEntryRole; import com.netgrif.application.engine.workflow.service.interfaces.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java b/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java index 60a3cf857f7..fed6dae2275 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java +++ b/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java @@ -8,7 +8,7 @@ import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.workflow.domain.IllegalMenuFileException; -import com.netgrif.application.engine.workflow.domain.menu.MenuEntry; +import com.netgrif.application.engine.menu.domain.MenuEntry; import java.io.IOException; import java.util.List; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml new file mode 100644 index 00000000000..0c0cfd74d7a --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -0,0 +1,1491 @@ +<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://petriflow.com/petriflow.schema.xsd"> + <id>menu_item</id> + <initials>MNI</initials> + <title>Menu Item + check_box_outline_blank + true + false + false + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + + menu_item_delete + + + removeItemChildCases() + removeViewCase() + + + + + + system + System + + + admin + Admin + + + { -> + + def childCaseIds = useCase.dataSet['childItemIds'].value + if (childCaseIds == null || childCaseIds.isEmpty()) { + return + } + + removeChildItemFromParent(useCase.dataSet['parentId'].value[0], useCase) + + def childCases = workflowService.findAllById(childCaseIds) + async.run { + childCases.each { + workflowService.deleteCase(it) + } + } + } + + + { -> + + def viewIdAsList = useCase.dataSet['view_configuration_id'].value + if (viewIdAsList == null || viewIdAsList.isEmpty()) { + return + } + + async.run { + workflowService.deleteCase(viewIdAsList[0]) + } + } + + + { boolean useTabIcon, boolean useTabbedView, String transId = "item_settings" -> + def settingsTrans = useCase.petriNet.transitions[transId] + + make [useCase.getField("use_tab_icon"), useCase.getField("tab_icon"), useCase.getField("tab_name")], + editable on settingsTrans when { useTabbedView && useTabIcon } + make useCase.getField("tab_icon_preview"), visible on settingsTrans when { useTabbedView && useTabIcon } + + make useCase.getField("use_tab_icon"), editable on settingsTrans when { useTabbedView && !useTabIcon } + make [useCase.getField("tab_icon_preview"), useCase.getField("tab_icon"), useCase.getField("tab_name")], + hidden on settingsTrans when { useTabbedView && !useTabIcon } + + make [useCase.getField("use_tab_icon"), useCase.getField("tab_icon_preview"), useCase.getField("tab_icon"), + useCase.getField("tab_name")], hidden on settingsTrans when { !useTabbedView } + } + + + { + -> + String query = String.format("processIdentifier:menu_item AND uriNodeId:%s AND dataSet.is_auto_select.booleanValue:true AND NOT stringId:%s", + useCase.uriNodeId, useCase.stringId) + def itemCase = findCaseElastic(query) + if (itemCase == null) { + return + } + + setData("item_settings", itemCase, [ + "is_auto_select": [ + "value": false, + "type": "boolean" + ] + ]) + } + + + + parentId + + <allowedNets> + <allowedNet>menu_item</allowedNet> + </allowedNets> + </data> + <data type="text"> + <id>move_previous_dest_uri</id> + <title/> + </data> + <data type="multichoice_map"> + <id>move_dest_uri</id> + <title name="move_dest_uri">Destination URI + List of nodes representing destination URI + + autocomplete + + + moveDestUri: f.move_dest_uri; + + String uriNodeId = elasticCaseService.findUriNodeId(useCase) + def node = uriService.findById(uriNodeId) + updateMultichoiceWithCurrentNode(moveDestUri, node) + + + prevDestUri: f.move_previous_dest_uri, + moveDestUri: f.move_dest_uri; + + String newUri = moveDestUri.value.join("/") + newUri = newUri.replace("//","/") + + String corrected = getCorrectedUri(newUri) + + if (corrected == newUri) { + def node = uriService.findByUri(newUri) + change moveDestUri options { findOptionsBasedOnSelectedNode(node) } + } else { + change moveDestUri value { splitUriPath(corrected) } + } + + + + move_dest_uri_new_node + New node to be added + Enter new node name + + + move_add_node + + <placeholder name="move_add_node">Add</placeholder> + <component> + <name>raised</name> + </component> + <action trigger="set"> + newNodeName: f.move_dest_uri_new_node, + selectedUri: f.move_dest_uri; + + if (newNodeName.value == null || newNodeName.value == "") { + return + } + + String prefixUri = selectedUri.value.join("/") + prefixUri = prefixUri.replace("//","/") + + String newUri = prefixUri + uriService.getUriSeparator() + newNodeName.value + def newNode = uriService.getOrCreate(newUri, com.netgrif.application.engine.petrinet.domain.UriContentType.CASE) + + change selectedUri value { splitUriPath(newNode.uriPath) } + + change newNodeName value { null } + </action> + </data> + <data type="i18n"> + <id>duplicate_new_title</id> + <title name="duplicate_new_title">Title of duplicated view + + + duplicate_view_identifier + View identifier + Must be unique + + + childItemIds + + <allowedNets> + <allowedNet>menu_item</allowedNet> + </allowedNets> + </data> + <data type="taskRef"> + <id>childItemForms</id> + <title/> + </data> + <data type="boolean" immediate="true"> + <id>hasChildren</id> + <title/> + </data> + <data type="button"> + <id>duplicate_reset_childItemIds</id> + <title/> + <action trigger="set"> + hasChildren: f.hasChildren, + childItemIds: f.childItemIds; + + change childItemIds value { [] } + change hasChildren value { false } + </action> + </data> + <data type="text" immediate="true"> + <id>menu_item_identifier</id> + <title name="menu_item_identifier">Menu item identifier + + + nodePath + Item URI + + 0 + + + nodePath: f.nodePath, + menu_item_identifier: f.menu_item_identifier; + + change menu_item_identifier value { + def idx = nodePath.value.lastIndexOf(uriService.getUriSeparator()) + return nodePath.value.substring(idx + 1) + } + + + + + + + + menu_icon + Menu icon identifier + Material icon identifier. List of icons with identifiers is available online. + + icon: f.this, + iconPreview: f.menu_icon_preview; + + changeCaseProperty "icon" about { icon.value; } + + if (icon.value == "") { + change iconPreview value {"""]]>} + return; + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + menu_icon_preview + Menu icon preview + + htmltextarea + + + + menu_name_as_visible + Name of the item + Is shown in the menu + + autocomplete + + + + menu_name + Name of the item + Will be shown in the menu + + menu_name_as_visible: f.menu_name_as_visible, + name: f.menu_name; + + changeCaseProperty "title" about { name.value } + change menu_name_as_visible choices { [name.value] } + change menu_name_as_visible value { name.value } + + + + add_allowed_roles + + <placeholder name="allow_roles">Allow view for roles</placeholder> + <action trigger="set"> + allowedRoles: f.allowed_roles, + processesAvailable: f.processes_available, + rolesAvailable: f.roles_available; + + change allowedRoles options {return configurableMenuService.addSelectedRoles(allowedRoles, processesAvailable, rolesAvailable)} + + change rolesAvailable value {[]} + change rolesAvailable options {[:]} + change processesAvailable value {null} + </action> + </data> + <data type="button"> + <id>remove_allowed_roles</id> + <title/> + <placeholder name="remove_from_allowed_roles">Remove from allowed roles</placeholder> + <action trigger="set"> + allowedRoles: f.allowed_roles, + processesAvailable: f.processes_available, + rolesAvailable: f.roles_available; + + change allowedRoles options {return configurableMenuService.removeSelectedRoles(allowedRoles)} + + change allowedRoles value {[]} + change rolesAvailable value {[]} + change rolesAvailable options {[:]} + change processesAvailable value {null} + </action> + </data> + <data type="button"> + <id>add_banned_roles</id> + <title/> + <placeholder name="ban_roles">Ban view for roles</placeholder> + <action trigger="set"> + bannedRoles: f.banned_roles, + processesAvailable: f.processes_available, + rolesAvailable: f.roles_available; + + change bannedRoles options {return configurableMenuService.addSelectedRoles(bannedRoles, processesAvailable, rolesAvailable)} + + change rolesAvailable value {[]} + change rolesAvailable options {[:]} + change processesAvailable value {null} + </action> + </data> + <data type="button"> + <id>remove_banned_roles</id> + <title/> + <placeholder name="remove_from_banned_roles">Remove from banned roles</placeholder> + <action trigger="set"> + bannedRoles: f.banned_roles, + processesAvailable: f.processes_available, + rolesAvailable: f.roles_available; + + change bannedRoles options { return configurableMenuService.removeSelectedRoles(bannedRoles) } + + change bannedRoles value { [] } + change rolesAvailable value { [] } + change rolesAvailable options { [:] } + change processesAvailable value { null } + </action> + </data> + <data type="enumeration_map" immediate="true"> + <id>processes_available</id> + <title name="available_processes">Your processes + Select a process containing roles you wish to add to allowed or banned roles lists. + + processes: f.this; + + change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } + + + processes: f.this, + allowedRoles: f.allowed_roles, + bannedRoles: f.banned_roles, + rolesAvailable: f.roles_available; + + if (processes.value != null) { + change rolesAvailable options { return configurableMenuService.getAvailableRolesFromNet(processes, allowedRoles, bannedRoles) } + } else { + change rolesAvailable options { [:] } + } + change rolesAvailable value { [] } + + + + roles_available + Available roles from selected process + + + allowed_roles + Allowed roles + List of roles allowed to view this menu entry. + + [:] + + + + banned_roles + Banned roles + List of roles not allowed to view this menu entry. + + [:] + + + + use_custom_view + Use custom view? + false + + + custom_view_selector + Custom view configuration selector + Example: "demo-tabbed-views" + + + is_auto_select + View auto selection + If selected, the view will be automatically opened + false + + is_auto_select: f.is_auto_select; + + if (!is_auto_select.value) + return + + removeAnyAutoSelectFlagInFolder() + + + + + use_tabbed_view + Do you want to use view with tabs? + + + + + tab_icon + Tab icon identifier + Material icon identifier. List of icons with identifiers is available online. + + icon: f.this, + iconPreview: f.tab_icon_preview; + + if (icon.value == "") { + change iconPreview value {"""]]>} + return; + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + tab_icon_preview + Tab icon preview + + htmltextarea + + + + use_tab_icon + Display tab icon? + true + + + tab_name + Name of the item + Will be shown in tab + + + view_configuration_type + + Pick view type + + menuItemService.getAvailableViewsAsOptions(false) + + + use_tabbed_view: f.use_tabbed_view, + view_configuration_type: f.view_configuration_type; + + if (view_configuration_type.options == null || view_configuration_type.options.isEmpty()) { + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value) } + } + + + + view_configuration_id + + <allowedNets> + <allowedNet>tabbed_case_view_configuration</allowedNet> + <allowedNet>tabbed_task_view_configuration</allowedNet> + </allowedNets> + </data> + <data type="taskRef"> + <id>view_configuration_form</id> + <title/> + </data> + <data type="button"> + <id>order_down</id> + <title/> + <placeholder>south</placeholder> + <component> + <name>icon</name> + <property key="stretch">true</property> + </component> + <action trigger="set"> + parentId: f.parentId; + + def parentCase = workflowService.findOne(parentId.value[0]) + def taskId = useCase.tasks.find { it.transition == "row_for_ordering" }.task + def taskRefValue = parentCase.dataSet['childItemForms'].value + int taskRefValueSize = taskRefValue.size() + def caseRefValue = parentCase.dataSet['childItemIds'].value + int caseRefValueSize = caseRefValue.size() + + int idxInTaskRef = taskRefValue.indexOf(taskId) + if (idxInTaskRef < taskRefValueSize - 1) { + Collections.swap(taskRefValue, idxInTaskRef, idxInTaskRef + 1) + } + + int idxInCaseRef = caseRefValue.indexOf(useCase.stringId) + if (idxInCaseRef < caseRefValueSize - 1) { + Collections.swap(caseRefValue, idxInCaseRef, idxInCaseRef + 1) + } + + setData("children_order", parentCase, [ + "childItemForms" : [ + "value" : taskRefValue, + "type" : "taskRef" + ], + "childItemIds" : [ + "value" : caseRefValue, + "type" : "caseRef" + ] + ]) + </action> + </data> + <data type="button"> + <id>order_up</id> + <title/> + <placeholder>north</placeholder> + <component> + <name>icon</name> + <property key="stretch">true</property> + </component> + <action trigger="set"> + parentId: f.parentId; + + def parentCase = workflowService.findOne(parentId.value[0]) + def taskId = useCase.tasks.find { it.transition == "row_for_ordering" }.task + def taskRefValue = parentCase.dataSet['childItemForms'].value + def caseRefValue = parentCase.dataSet['childItemIds'].value + + int idxInTaskRef = taskRefValue.indexOf(taskId) + if (idxInTaskRef > 0) { + Collections.swap(taskRefValue, idxInTaskRef - 1, idxInTaskRef) + } else { + return + } + + int idxInCaseRef = caseRefValue.indexOf(useCase.stringId) + if (idxInCaseRef > 0) { + Collections.swap(caseRefValue, idxInCaseRef - 1, idxInCaseRef) + } else { + return + } + + setData("children_order", parentCase, [ + "childItemForms" : [ + "value" : taskRefValue, + "type" : "taskRef" + ], + "childItemIds" : [ + "value" : caseRefValue, + "type" : "caseRef" + ] + ]) + </action> + </data> + + <!-- I18NS --> + <i18n locale="sk"> + <i18nString name="icon_preview">Náhľad ikony</i18nString> + <i18nString name="icon_identifier">Identifikátor ikony</i18nString> + <i18nString name="icon_identifier_desc">Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online.</i18nString> + <i18nString name="allow_roles">Pridaj k povoleným roliam</i18nString> + <i18nString name="remove_from_allowed_roles">Odstráň z povolených rolí</i18nString> + <i18nString name="ban_roles">Pridaj k zakázaným roliam</i18nString> + <i18nString name="remove_from_banned_roles">Odstráň zo zakázaných rolí</i18nString> + <i18nString name="available_processes">Vaše procesy</i18nString> + <i18nString name="available_processes_desc">Vyberte proces obsahujúci roly ktoré chcete pridať do zoznamu povolených alebo zakázaných rolí.</i18nString> + <i18nString name="available_roles">Dostupné roly</i18nString> + <i18nString name="move_dest_uri">Cieľové URI</i18nString> + <i18nString name="duplicate_new_title">Názov duplikovanej položky</i18nString> + <i18nString name="duplicate_view_identifier">Identifikátor duplikovanej položky</i18nString> + <i18nString name="duplicate_view_identifier_desc">Musí byť jedinečný</i18nString> + <i18nString name="name">Názov položky</i18nString> + <i18nString name="name_desc">Bude zobrazený v menu</i18nString> + <i18nString name="tab_icon">Identifikátor ikony v karte</i18nString> + <i18nString name="tab_icon_identifier_desc">Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online.</i18nString> + <i18nString name="display_tab_icon">Zobraziť ikonu v karte?</i18nString> + <i18nString name="tab_name">Názov položky</i18nString> + <i18nString name="tab_name_desc">Bude zobrazený v karte</i18nString> + <i18nString name="use_custom_view">Použiť vlastné zobrazenie?</i18nString> + <i18nString name="custom_view_selector">Konfiguračný identifikátor vlastného zobrazenia</i18nString> + <i18nString name="custom_view_selector_desc">Napríklad: "demo-tabbed-views"</i18nString> + <i18nString name="item_settings">Nastavenie položky</i18nString> + <i18nString name="roles_management_title">Roly</i18nString> + <i18nString name="move_item">Presunúť položku</i18nString> + <i18nString name="move_item_finish">Presunúť</i18nString> + <i18nString name="duplicate_item">Duplikovať položku</i18nString> + <i18nString name="duplicate_item_finish">Duplikovať</i18nString> + <i18nString name="roles_allowed">Povolené roly</i18nString> + <i18nString name="roles_allowed_desc">Zoznam povolených rolí, ktoré môžu zobraziť túto položku</i18nString> + <i18nString name="roles_banned">Zakázané roly</i18nString> + <i18nString name="roles_banned_desc">Zoznam zakázaných rolí, ktoré nemôžu zobraziť túto položku</i18nString> + <i18nString name="item_settings_general">Všeobecné</i18nString> + <i18nString name="menu_item_identifier">Identifikátor položky</i18nString> + <i18nString name="nodePath">URI položky</i18nString> + <i18nString name="move_dest_uri_new_node">Nový uzol</i18nString> + <i18nString name="move_dest_uri_new_node_desc">Uveďte názov uzlu, ktorý chcete pridať</i18nString> + <i18nString name="move_dest_uri_desc">Zoznam uzlov reprezentujúce cieľovú URI</i18nString> + <i18nString name="move_add_node">Pridať</i18nString> + <i18nString name="is_auto_select">Automatické zvolenie zobrazenia</i18nString> + <i18nString name="is_auto_select_desc">Po automatickom zvolení sa dané zobrazenie používateľovi otvorí</i18nString> + </i18n> + <i18n locale="de"> + <i18nString name="icon_preview">Ikonevorschau</i18nString> + <i18nString name="icon_identifier">Ikone ID</i18nString> + <i18nString name="icon_identifier_desc">Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar.</i18nString> + <i18nString name="allow_roles">Zu zulässigen Rollen hinzufügen</i18nString> + <i18nString name="remove_from_allowed_roles">Aus zulässigen Rollen entfernen</i18nString> + <i18nString name="ban_roles">Zu verbotenen Rollen hinzufügen</i18nString> + <i18nString name="remove_from_banned_roles">Aus verbotenen Rollen entfernen</i18nString> + <i18nString name="available_processes">Ihre Prozesse</i18nString> + <i18nString name="available_processes_desc">Wählen Sie einen Prozess mit Rollen aus, die Sie zu Listen mit zulässigen oder verbotenen Rollen hinzufügen möchten.</i18nString> + <i18nString name="available_roles">Verfügbare Rollen</i18nString> + <i18nString name="tab_icon_identifier_desc">Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar.</i18nString> + <i18nString name="custom_view_selector_desc">Beispiel: "demo-tabbed-views"</i18nString> + <i18nString name="roles_management_title">Rollen</i18nString> + <i18nString name="roles_allowed">Zulässige Rollen</i18nString> + <i18nString name="item_settings_general">Allgemein</i18nString> + <i18nString name="menu_item_identifier">Identifikationsnummer des Menüeintrages</i18nString> + <i18nString name="nodePath">Menüeintrag-URI</i18nString> + <i18nString name="move_dest_uri_new_node">Neuer Knoten</i18nString> + <i18nString name="move_add_node">Hinzufügen</i18nString> + <i18nString name="move_dest_uri">Ziel URI</i18nString> + <i18nString name="duplicate_new_title">Titel der kopierten Ansicht</i18nString> + <i18nString name="duplicate_view_identifier">Identifikator der kopierten Ansicht</i18nString> + <i18nString name="duplicate_view_identifier_desc">Muss einzigartig sein</i18nString> + <i18nString name="name">Titel des Eintrages</i18nString> + <i18nString name="name_desc">Wird im Menü angezeigt</i18nString> + <i18nString name="tab_icon">Ikonen Identifikator der Registerkarte</i18nString> + <i18nString name="display_tab_icon">Zeige die Registerkarte Ikone an?</i18nString> + <i18nString name="tab_name">Titel der Registerkarte</i18nString> + <i18nString name="tab_name_desc">Wird in der Registerkarte angezeigt</i18nString> + <i18nString name="use_custom_view">Eigener Ansicht anwenden?</i18nString> + <i18nString name="custom_view_selector">Konfigurationsidentifikator der eigenen Ansicht</i18nString> + <i18nString name="item_settings">Menüeintrageinstellungen</i18nString> + <i18nString name="move_item">Menüeintrag verschieben</i18nString> + <i18nString name="move_item_finish">verschieben</i18nString> + <i18nString name="duplicate_item">Menüeintrag duplizieren</i18nString> + <i18nString name="duplicate_item_finish">duplizieren</i18nString> + <i18nString name="roles_allowed_desc">Rollen mit Zugriff auf diesen Menüeintrag</i18nString> + <i18nString name="roles_banned">Verbotene Rollen</i18nString> + <i18nString name="roles_banned_desc">Rollen, für die wird den Menüeintrag ausgeblendet</i18nString> + <i18nString name="move_dest_uri_new_node_desc">Nächste URI-Teil angeben</i18nString> + <i18nString name="move_dest_uri_desc">Teile der Ziel URI</i18nString> + <i18nString name="is_auto_select">Automatische Anzeigeauswahl</i18nString> + <i18nString name="is_auto_select_desc">Wenn ausgewählt, wird die Ansicht automatisch geöffnet</i18nString> + </i18n> + + <!-- TRANSITIONS --> + <transition> + <id>initialize</id> + <x>340</x> + <y>220</y> + <label>initialize [await sync]</label> + <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + <view>true</view> + <cancel>true</cancel> + <assign>true</assign> + </logic> + </roleRef> + </transition> + <transition> + <id>data_sync</id> + <x>340</x> + <y>340</y> + <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + </transition> + + <transition> + <id>item_settings</id> + <x>460</x> + <y>100</y> + <label name="item_settings">Item settings</label> + <icon>settings</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + <view>true</view> + <cancel>true</cancel> + <assign>true</assign> + </logic> + </roleRef> + <dataGroup> + <id>pre_general</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>menu_item_identifier</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>nodePath</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <dataGroup> + <id>general_0</id> + <cols>4</cols> + <layout>grid</layout> + <title name="item_settings_general">General + + menu_name + + editable + + + 0 + 0 + 1 + 2 + + outline + + + + menu_icon + + editable + + + 2 + 0 + 1 + 1 + + outline + + + + menu_icon_preview + + visible + + + 3 + 0 + 1 + 1 + + standard + + + + is_auto_select + + editable + + + 1 + 2 + 1 + 1 + + standard + + + + + roles_management + 5 + grid + Roles + + processes_available + + editable + + + 0 + 0 + 2 + 1 + 0 + + outline + + + + roles_available + + editable + + + 1 + 0 + 2 + 1 + 0 + + outline + + + + add_allowed_roles + + editable + + + 2 + 0 + 1 + 1 + 0 + + + + + allowed_roles + + editable + + + 3 + 0 + 1 + 1 + 0 + + outline + + + + remove_allowed_roles + + editable + + + 4 + 0 + 1 + 1 + 0 + + + + + add_banned_roles + + editable + + + 2 + 1 + 1 + 1 + 0 + + + + + banned_roles + + editable + + + 3 + 1 + 1 + 1 + 0 + + outline + + + + remove_banned_roles + + editable + + + 4 + 1 + 1 + 1 + 0 + + + + + + configuration_view + 4 + grid + + View configuration + + use_custom_view + + editable + + + 0 + 0 + 1 + 1 + + outline + + + 0 + + + trans: t.this, + useTabIcon: f.use_tab_icon, + use_tabbed_view: f.use_tabbed_view, + view_configuration_form: f.view_configuration_form, + view_configuration_type: f.view_configuration_type, + tabIconPreview: f.tab_icon_preview, + tabName: f.tab_name, + tabIcon: f.tab_icon, + use: f.use_custom_view, + selector: f.custom_view_selector; + + if (use.value) { + make selector, editable on trans when { true } + make [useTabIcon, tabIconPreview, tabName, tabIcon, use_tabbed_view, view_configuration_form, + view_configuration_type], hidden on trans when { true } + // todo remove configuration or keep it? + } else { + manageBehaviorOfTabFields(useTabIcon.value, use_tabbed_view.value) + make [use_tabbed_view, view_configuration_form, view_configuration_type], editable on trans when { true } + make selector, hidden on trans when { true } + } + + + + + + custom_view_selector + + hidden + + + 1 + 0 + 1 + 3 + + outline + + + + use_tabbed_view + + editable + + + 1 + 0 + 1 + 1 + + standard + + + 0 + + + use_tabbed_view: f.use_tabbed_view, + use_tab_icon: f.use_tab_icon, + view_configuration_type: f.view_configuration_type; + + manageBehaviorOfTabFields(use_tab_icon.value, use_tabbed_view.value) + + change view_configuration_type value { null } + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value) } + + + + + + tab_name + + hidden + + + 2 + 0 + 1 + 1 + + outline + + + + use_tab_icon + + hidden + + + 3 + 0 + 1 + 1 + 0 + + + + 0 + + + use_tabbed_view: f.use_tabbed_view, + useIcon: f.use_tab_icon; + + manageBehaviorOfTabFields(useIcon.value, use_tabbed_view.value) + + + + + + tab_icon + + hidden + + + 0 + 1 + 1 + 1 + + outline + + + + tab_icon_preview + + hidden + + + 1 + 1 + 1 + 2 + + standard + + + + view_configuration_type + + editable + + + 0 + 2 + 1 + 4 + + outline + + + 0 + + + view_configuration_type: f.view_configuration_type, + view_configuration_form: f.view_configuration_form, + view_configuration_id: f.view_configuration_id; + + if (view_configuration_type.value == null || view_configuration_type.value == "") { + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + return + } + + def configurationCase = createCase(view_configuration_type.value + "_configuration") + def initTask = assignTask("initialize", configurationCase) + finishTask(initTask) + change view_configuration_id value { [configurationCase.stringId] } + change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + + + + + + view_configuration_form + + editable + + + 0 + 3 + 1 + 4 + + outline + + + + + + + move_item + 580 + 100 + + move_down + auto + + admin + + true + true + true + true + + + + move + 4 + grid + + move_dest_uri + + editable + required + + + 0 + 0 + 1 + 2 + + outline + + + + move_dest_uri_new_node + + editable + + + 2 + 0 + 1 + 1 + + outline + + + + move_add_node + + editable + + + 3 + 0 + 1 + 1 + + outline + + + + + finish + + + dest: f.move_dest_uri; + + if (dest.value == null || dest.value == []) { + throw new IllegalArgumentException("URI must not be empty!") + } + + String newUri = dest.value.join("/") + newUri = newUri.replace("//","/") + + changeMenuItem useCase uri { newUri } + + + Move + + + + + duplicate_item + 580 + 340 + + content_copy + auto + + admin + + true + true + true + true + + + + duplicate + 4 + grid + + duplicate_new_title + + editable + required + + + 0 + 0 + 1 + 4 + + outline + + + + duplicate_view_identifier + + editable + required + + + 0 + 1 + 1 + 4 + + outline + + + + + finish + + + identifier: f.duplicate_view_identifier, + title: f.duplicate_new_title; + + duplicateMenuItem(useCase, title.value, identifier.value) + + + Duplicate + + + + + children_order + 580 + 220 + + low_priority + auto + + admin + + true + true + true + true + + + + children_order_0 + 4 + grid + + childItemForms + + editable + + forms: f.childItemForms, + ids: f.childItemIds; + + def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } + change forms value { orderedTaskIds } + + + + 0 + 0 + 1 + 4 + + outline + + + + + + row_for_ordering + 741 + 219 + + + system + + true + true + true + true + + + + row_for_ordering_0 + 6 + grid + + menu_item_identifier + + visible + + + 0 + 0 + 1 + 2 + + outline + + + + menu_name_as_visible + + visible + + + 2 + 0 + 1 + 2 + + outline + + + + order_down + + editable + + + 4 + 0 + 1 + 1 + 1 + + outline + + + + order_up + + editable + + + 5 + 0 + 1 + 1 + 1 + + outline + + + + + finish + + + + delegate + + + + + + + uninitialized + 220 + 220 + + 1 + false + + + initialized + 460 + 220 + + 0 + false + + + + + a1 + regular + uninitialized + initialize + 1 + + + a7 + read + initialized + item_settings + 1 + + + a8 + regular + initialize + initialized + 1 + + + a9 + read + initialized + move_item + 1 + + + a10 + read + initialized + duplicate_item + 1 + + + a13 + read + initialized + children_order + 1 + + \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml new file mode 100644 index 00000000000..1fdc487b7e0 --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -0,0 +1,933 @@ + + tabbed_case_view_configuration + TCV + Tabbed case view configuration + check_box_outline_blank + true + false + false + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + + view_delete + + + removeViewCase() + + + + + + system + System + + + admin + Admin + + + + { + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete, + com.netgrif.application.engine.petrinet.domain.dataset.TaskField previewTaskRef, + com.netgrif.application.engine.petrinet.domain.dataset.CaseField selectedFilterRef, + com.netgrif.application.engine.petrinet.domain.dataset.ButtonField updateBtn, + com.netgrif.application.engine.petrinet.domain.Transition trans + -> + if (filterAutocomplete.getOptions().containsKey(filterAutocomplete.value)) { + change previewTaskRef value { + return [findTask({it.caseId.eq(filterAutocomplete.value).and(it.transitionId.eq("view_filter"))}).stringId] + } + make updateBtn,editable on trans when { true } + } else { + change filterAutocomplete options { + def findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) + && filterCase.dataSet["filter_type"].value == "Case" } + return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") + .findAll(findAllPredicate) + .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) + } + change previewTaskRef value { [] } + make updateBtn,visible on trans when { true } + } + } + + + { -> + + def viewIdAsList = useCase.dataSet['view_configuration_id'].value + if (viewIdAsList == null || viewIdAsList.isEmpty()) { + return + } + + async.run { + workflowService.deleteCase(viewIdAsList[0]) + } + } + + + { + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField toBeUpdated, + com.netgrif.application.engine.petrinet.domain.dataset.MultichoiceMapField valueSelector, + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField optionsHolder + -> + def existingOptions = optionsHolder.options + def selectedValues = valueSelector.value + def newOptions = [:] + + if (selectedValues != null) { + existingOptions.each { key, value -> + if (selectedValues.contains(key)) { + newOptions.put(key, value) + } + } + } + + if (!newOptions.containsKey(toBeUpdated.value)) { + change toBeUpdated value { null } + } + + change toBeUpdated options { newOptions } + } + + + + + selected_filter_preview + + </data> + <data type="taskRef"> + <id>current_filter_preview</id> + <title/> + </data> + <data type="i18n"> + <id>filter_header</id> + <title/> + <init name="filter_header">Current filter</init> + <component> + <name>divider</name> + </component> + </data> + <data type="text"> + <id>new_filter_id</id> + <title/> + </data> + <data type="boolean" immediate="true"> + <id>contains_filter</id> + <title/> + <init>false</init> + </data> + <data type="enumeration_map"> + <id>filter_autocomplete_selection</id> + <title name="filter_autocomplete_selection">Select new filter + + autocomplete_dynamic + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + + update_filter + + <placeholder name="update_filter">Update view with selected filter</placeholder> + <component> + <name>raised</name> + </component> + <action trigger="set"> + trans: t.settings, + update_filter: f.update_filter, + contains_filter: f.contains_filter, + filter_case: f.filter_case, + filterAutocomplete: f.filter_autocomplete_selection; + + boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" + if (containsFilter) { + def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) + if (filterCase.dataSet["filter_type"].value != "Case") { + throw new IllegalArgumentException("Filter is of wrong type. Only filter of Case type allowed.") + } + } + + change contains_filter value { containsFilter } + change filter_case value { [filterAutocomplete.value] } + change filterAutocomplete value { "" } + make update_filter,visible on trans when { true } + </action> + </data> + <data type="caseRef"> + <id>filter_case</id> + <title/> + <action trigger="set"> + filterTaskRef: f.current_filter_preview, + contains_filter: f.contains_filter, + filterCaseRef: f.filter_case; + + if (filterCaseRef.value == null || filterCaseRef.value == []) { + change filterTaskRef value { [] } + change contains_filter value { false } + return + } + + def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) + change filterTaskRef value { + return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] + } + change contains_filter value { true } + </action> + <allowedNets> + <allowedNet>filter</allowedNet> + </allowedNets> + </data> + + <!-- VIEW CONFIGURATION DATA --> + <data type="enumeration_map" immediate="true"> + <id>view_search_type</id> + <title name="view_search_type">Search type for case view + + + + + + fulltext_advanced + + + create_case_button_title + "New case" button title + + + create_case_button_icon_preview + Icon preview + add]]> + + htmltextarea + + + + create_case_button_icon + "New case" button icon identifier + add + + create_case_button_icon_preview: f.create_case_button_icon_preview, + create_case_button_icon: f.create_case_button_icon; + + + if (create_case_button_icon.value == "") { + change create_case_button_icon_preview value {"""]]>} + return; + } + + change create_case_button_icon_preview value { + """]]> + create_case_button_icon.value + """]]> + } + + + + show_create_case_button + Show create case button? + true + + + require_title_in_creation + Require title input in case creation? + true + + + banned_nets_in_creation + Banned processes for creation + Write down process identifiers separated by comma. Example: mynet1,mynet2 + + bannedNets: f.this; + + String trimmed = bannedNets.value?.replaceAll("\\s","") + if (bannedNets.value != trimmed) { + change bannedNets value { trimmed } + } + + + + view_header + + <init name="view_header">Case view</init> + <component> + <name>divider</name> + </component> + </data> + <data type="boolean" immediate="true"> + <id>show_more_menu</id> + <title name="show_more_menu">Show more menu for case item? + false + + + allow_header_table_mode + Allow table mode for headers? + true + + + headers_mode + Header mode + + + + + + sort,edit,search + + headersMode: f.headers_mode, + defaultMode: f.headers_default_mode, + holder: f.headers_options_holder; + + updateOptionsBasedOnValue(defaultMode, headersMode, holder) + + + headersMode: f.headers_mode, + defaultMode: f.headers_default_mode, + holder: f.headers_options_holder; + + updateOptionsBasedOnValue(defaultMode, headersMode, holder) + + + + headers_default_mode + Default header mode + + + + + + sort + + + headers_options_holder + + <options> + <option key="sort" name="sort">Sort</option> + <option key="search" name="search">Search</option> + <option key="edit" name="edit">Edit</option> + </options> + </data> + <data type="boolean" immediate="true"> + <id>is_header_mode_changeable</id> + <title name="is_header_mode_changeable">Can header mode be changed? + true + + + use_case_default_headers + Use custom default headers? + true + + + default_headers + Set default headers + Example: "meta-title,meta-visualId" + + defaultHeaders: f.this; + + String trimmed = defaultHeaders.value?.replaceAll("\\s","") + if (defaultHeaders.value != trimmed) { + change defaultHeaders value { trimmed } + } + + + + view_configuration_type + + Pick view type + + menuItemService.getAvailableViewsAsOptions(false, useCase.processIdentifier) + + + view_configuration_type: f.view_configuration_type; + + if (view_configuration_type.options == null || view_configuration_type.options.isEmpty()) { + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(true) } + } + + + + view_configuration_id + + <allowedNets> + <allowedNet>tabbed_task_view_configuration</allowedNet> + </allowedNets> + </data> + <data type="taskRef"> + <id>view_configuration_form</id> + <title/> + </data> + + <!-- I18NS --> +// todo prejst preklady + <i18n locale="sk"> + <i18nString name="create_case_button_title">Názov tlačidla "Nová inštancia"</i18nString> + <i18nString name="create_case_button_icon">Identifikátor ikony tlačidla "Nová inštancia"</i18nString> + <i18nString name="create_case_button_icon_preview">Náhľad ikony</i18nString> + <i18nString name="default_headers">Predvolené hlavičky</i18nString> + <i18nString name="default_headers_desc">Napríklad: "meta-title,meta-visualId"</i18nString> + <i18nString name="filter_autocomplete_selection">Zvoľte nový filter</i18nString> + <i18nString name="update_filter">Aktualizovať zobrazenie s vybraným filtrom</i18nString> + <i18nString name="view_search_type">Typ vyhľadávania prípadov</i18nString> + <i18nString name="hidden">Skryté</i18nString> + <i18nString name="fulltext">Fulltext</i18nString> + <i18nString name="fulltext_advanced">Fulltext a rozšírené</i18nString> + <i18nString name="require_title_in_creation">Vyžadovať názov inštancii pri vytváraní?</i18nString> + <i18nString name="banned_nets_in_creation">Zakázané siete pri vytváraní</i18nString> + <i18nString name="banned_nets_in_creation_desc">Uveďte identifikátory procesov oddelené čiarkou. Napríklad: mynet1,mynet2</i18nString> + <i18nString name="show_create_case_button">Zobraziť tlačidlo na vytvorenie prípadu?</i18nString> + <i18nString name="show_more_menu">Zobrazovať menu pre prípadovú položku?</i18nString> + <i18nString name="sort">Zoraďovanie</i18nString> + <i18nString name="search">Vyhľadávanie</i18nString> + <i18nString name="edit">Upravovanie</i18nString> + <i18nString name="headers_mode">Mód hlavičiek</i18nString> + <i18nString name="headers_default_mode">Predvolený mód hlavičiek</i18nString> + <i18nString name="is_header_mode_changeable">Môže byť mód hlavičiek zmenený?</i18nString> + <i18nString name="allow_header_table_mode">Povoliť tabuľkový mód pre hlavičky?</i18nString> + <i18nString name="use_default_headers">Použiť vlastné predvolené hlavičky?</i18nString> + <i18nString name="item_settings">Nastavenie položky</i18nString> + <i18nString name="filter_update_title">Filter</i18nString> + <i18nString name="filter_header">Súčasný filter</i18nString> + <i18nString name="view_header">Zobrazenie prípadov</i18nString> + <i18nString name="item_settings_general">Všeobecné</i18nString> + </i18n> + <i18n locale="de"> + <i18nString name="create_case_button_title">Schaltflächentitel "Neuer Fall"</i18nString> + <i18nString name="create_case_button_icon">Ikone ID</i18nString> + <i18nString name="create_case_button_icon_preview">Ikonevorschau</i18nString> + <i18nString name="default_headers">Anzuzeigende Attributmenge auswählen</i18nString> + <i18nString name="filter_autocomplete_selection">Neue Filter auswählen</i18nString> + <i18nString name="default_headers_desc">Beispiel: "meta-title,meta-visualId"</i18nString> + <i18nString name="hidden">Versteckt</i18nString> + <i18nString name="fulltext">Einfacher Suchmodus</i18nString> + <i18nString name="sort">Sortieren</i18nString> + <i18nString name="search">Suchen</i18nString> + <i18nString name="edit">Bearbeiten</i18nString> + <i18nString name="headers_mode">Kopfzeilenmodus</i18nString> + <i18nString name="headers_default_mode">Standardkopfzeilenmodus</i18nString> + <i18nString name="is_header_mode_changeable">Erlaube Änderung des Kopfzeilenmodus?</i18nString> + <i18nString name="allow_header_table_mode">Erlaube Tabellenmodus?</i18nString> + <i18nString name="use_default_headers">Eigene Kopfzeilen verwenden?</i18nString> + <i18nString name="filter_update_title">Filter</i18nString> + <i18nString name="filter_header">Aktueller Filter</i18nString> + <i18nString name="item_settings_general">Allgemein</i18nString> + <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> + <i18nString name="view_search_type">Suchmodus im Fallansicht</i18nString> + <i18nString name="fulltext_advanced">Einfacher und erweiterter Suchmodus</i18nString> + <i18nString name="require_title_in_creation">Erforde den Titel beim erzeugen von Fällen?</i18nString> + <i18nString name="banned_nets_in_creation">Ausgeschlossene Prozesse</i18nString> + <i18nString name="banned_nets_in_creation_desc">Trenne die Prozessidentifikatoren mit einer Komma. z.B.: netz1,netz2</i18nString> + <i18nString name="show_create_case_button">Schaltfläche „Fall erstellen“ anzeigen?</i18nString> + <i18nString name="show_more_menu">"Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen</i18nString> + <i18nString name="item_settings">Menüeintrageinstellungen</i18nString> + <i18nString name="view_header">Fallansicht</i18nString> + </i18n> + + <transition> + <id>initialize</id> + <x>368</x> + <y>208</y> + <label>initialize [await sync]</label> + <icon>hourglass_empty</icon> + </transition> + <transition> + <id>data_sync</id> + <x>368</x> + <y>328</y> + <label>Data sync</label> + </transition> + <transition> + <id>settings</id> + <x>496</x> + <y>112</y> +<!-- todo preklad--> + <label>Settings</label> + <icon>settings</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + <view>true</view> + <cancel>true</cancel> + <assign>true</assign> + </logic> + </roleRef> + <dataGroup> + <id>form_title</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>view_header</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <dataGroup> + <id>filter_update</id> + <cols>4</cols> + <layout>grid</layout> + <title name="filter_update_title">Filter + + filter_autocomplete_selection + + editable + + + 0 + 0 + 1 + 3 + + outline + + + + update_filter + + visible + + + 3 + 0 + 1 + 1 + + standard + + + + selected_filter_preview + + visible + + + 0 + 1 + 1 + 4 + + standard + + + + + current_filter + 4 + grid + + filter_header + + visible + + + 0 + 0 + 1 + 4 + + outline + + + + current_filter_preview + + visible + + + 0 + 1 + 1 + 4 + + standard + + + + + view_dataGroup + 4 + grid + + view_search_type + + editable + required + + + 0 + 0 + 1 + 2 + + outline + + + + show_create_case_button + + editable + required + + + 2 + 0 + 1 + 1 + + outline + + + + show_more_menu + + editable + required + + + 3 + 0 + 1 + 1 + + outline + + + + create_case_button_title + + editable + + + 0 + 1 + 1 + 1 + 0 + + outline + + + + create_case_button_icon + + editable + + + 1 + 1 + 1 + 1 + 0 + + outline + + + + create_case_button_icon_preview + + visible + + + 2 + 1 + 1 + 1 + 0 + + standard + + + + require_title_in_creation + + editable + + + 3 + 1 + 1 + 1 + 0 + + standard + + + + banned_nets_in_creation + + editable + + + 0 + 2 + 1 + 4 + 0 + + outline + + + + + view_headers + 5 + grid + + is_header_mode_changeable + + editable + required + + trans: t.this, + isChangeable: f.is_header_mode_changeable, + mode: f.headers_mode, + defaultMode: f.headers_default_mode; + + make [mode, defaultMode], editable on trans when { isChangeable.value } + make [mode, defaultMode], required on trans when { isChangeable.value } + + make [mode, defaultMode], hidden on trans when { !isChangeable.value } + make [mode, defaultMode], optional on trans when { !isChangeable.value } + + + + 0 + 0 + 1 + 1 + 0 + + outline + + + + allow_header_table_mode + + editable + required + + + 1 + 0 + 1 + 1 + 0 + + outline + + + + headers_mode + + editable + required + + + 2 + 0 + 1 + 2 + 0 + + outline + + + + headers_default_mode + + editable + required + + + 4 + 0 + 1 + 1 + 0 + + outline + + + + use_case_default_headers + + editable + + trans: t.this, + use: f.use_case_default_headers, + headers: f.default_headers; + + make headers,editable on trans when { use.value } + make headers,visible on trans when { !use.value } + + + + 0 + 1 + 1 + 1 + 0 + + outline + + + + default_headers + + editable + + + 1 + 1 + 1 + 4 + 0 + + outline + + + + + associated_view + 4 + grid + + Next view + + view_configuration_type + + editable + + + 0 + 0 + 1 + 4 + 0 + + outline + + + 0 + + + view_configuration_type: f.view_configuration_type, + view_configuration_form: f.view_configuration_form, + view_configuration_id: f.view_configuration_id; + + if (view_configuration_type.value == null || view_configuration_type.value == "") { + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + return + } + + def configurationCase = createCase(view_configuration_type.value + "_configuration") + def initTask = assignTask("initialize", configurationCase) + finishTask(initTask) + change view_configuration_id value { [configurationCase.stringId] } + change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + + + + + + view_configuration_form + + editable + + + 0 + 1 + 1 + 4 + 0 + + outline + + + + + + initialized + 496 + 208 + + 0 + false + + + uninitialized + 240 + 208 + + 1 + false + + + a1 + read + initialized + settings + 1 + + + a2 + regular + uninitialized + initialize + 1 + + + a3 + regular + initialize + initialized + 1 + + \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml new file mode 100644 index 00000000000..d969f5db746 --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -0,0 +1,716 @@ + + tabbed_task_view_configuration + TTV + Tabbed task view configuration + check_box_outline_blank + true + false + false + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + system + System + + + admin + Admin + + + + { + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete, + com.netgrif.application.engine.petrinet.domain.dataset.TaskField previewTaskRef, + com.netgrif.application.engine.petrinet.domain.dataset.CaseField selectedFilterRef, + com.netgrif.application.engine.petrinet.domain.dataset.ButtonField updateBtn, + com.netgrif.application.engine.petrinet.domain.Transition trans + -> + if (filterAutocomplete.getOptions().containsKey(filterAutocomplete.value)) { + change previewTaskRef value { + return [findTask({it.caseId.eq(filterAutocomplete.value).and(it.transitionId.eq("view_filter"))}).stringId] + } + make updateBtn,editable on trans when { true } + } else { + change filterAutocomplete options { + def findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) + && filterCase.dataSet["filter_type"].value == "Task" } + def options = findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") + .findAll(findAllPredicate) + .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) + return options + } + change previewTaskRef value { [] } + make updateBtn,visible on trans when { true } + } + } + + + { + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField toBeUpdated, + com.netgrif.application.engine.petrinet.domain.dataset.MultichoiceMapField valueSelector, + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField optionsHolder + -> + def existingOptions = optionsHolder.options + def selectedValues = valueSelector.value + def newOptions = [:] + + if (selectedValues != null) { + existingOptions.each { key, value -> + if (selectedValues.contains(key)) { + newOptions.put(key, value) + } + } + } + + if (!newOptions.containsKey(toBeUpdated.value)) { + change toBeUpdated value { null } + } + + change toBeUpdated options { newOptions } + } + + + + + selected_filter_preview + + </data> + <data type="taskRef"> + <id>current_filter_preview</id> + <title/> + </data> + <data type="i18n"> + <id>filter_header</id> + <title/> + <init name="filter_header">Current filter</init> + <component> + <name>divider</name> + </component> + </data> + <data type="text"> + <id>new_filter_id</id> + <title/> + </data> + <data type="boolean" immediate="true"> + <id>contains_filter</id> + <title/> + <init>false</init> + </data> + <data type="enumeration_map"> + <id>filter_autocomplete_selection</id> + <title name="filter_autocomplete_selection">Select new filter + + autocomplete_dynamic + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + + update_filter + + <placeholder name="update_filter">Update view with selected filter</placeholder> + <component> + <name>raised</name> + </component> + <action trigger="set"> + trans: t.settings, + update_filter: f.update_filter, + contains_filter: f.contains_filter, + filter_case: f.filter_case, + filterAutocomplete: f.filter_autocomplete_selection; + + boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" + if (containsFilter) { + def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) + if (filterCase.dataSet["filter_type"].value != "Task") { + throw new IllegalArgumentException("Filter is of wrong type. Only filter of Task type allowed.") + } + } + + change contains_filter value { containsFilter } + change filter_case value { [filterAutocomplete.value] } + change filterAutocomplete value { "" } + make update_filter,visible on trans when { true } + </action> + </data> + <data type="caseRef"> + <id>filter_case</id> + <title/> + <action trigger="set"> + trans: t.settings, + filterHeader: f.filter_header, + removeButton: f.remove_filter, + contains_filter: f.contains_filter, + mergeFilters: f.merge_filters, + filterTaskRef: f.current_filter_preview, + filterCaseRef: f.filter_case; + + if (filterCaseRef.value == null || filterCaseRef.value == []) { + make [mergeFilters, removeButton], hidden on trans when { true } + make filterHeader, hidden on trans when { true } + change filterTaskRef value { [] } + change contains_filter value { false } + return + } + + def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) + change filterTaskRef value { + return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] + } + + make [mergeFilters, removeButton], editable on trans when { true } + make filterHeader,visible on trans when { true } + change contains_filter value { true } + </action> + <allowedNets> + <allowedNet>filter</allowedNet> + </allowedNets> + </data> + <data type="button"> + <id>remove_filter</id> + <title/> + <placeholder name="remove_filter">Remove filter</placeholder> + <component> + <name>raised</name> + </component> + <action trigger="set"> + filterCase: f.filter_case; + + change filterCase value { [] } + </action> + </data> + <data type="boolean" immediate="true"> + <id>merge_filters</id> + <title name="merge_filters">Merge with base filter? + true + + + + + view_header + + <init name="view_header">Task view</init> + <component> + <name>divider</name> + </component> + </data> + <data type="enumeration_map" immediate="true"> + <id>view_search_type</id> + <title name="view_search_type">Search type for task view + + + + + + fulltext_advanced + + + headers_mode + Header mode + + + + + sort,edit + + headersMode: f.headers_mode, + defaultMode: f.headers_default_mode, + holder: f.headers_options_holder; + + updateOptionsBasedOnValue(defaultMode, headersMode, holder) + + + headersMode: f.headers_mode, + defaultMode: f.headers_default_mode, + holder: f.headers_options_holder; + + updateOptionsBasedOnValue(defaultMode, headersMode, holder) + + + + headers_default_mode + Default header mode + + + + + sort + + + headers_options_holder + + <options> + <option key="sort" name="sort">Sort</option> + <option key="edit" name="edit">Edit</option> + </options> + </data> + <data type="boolean" immediate="true"> + <id>is_header_mode_changeable</id> + <title name="is_header_mode_changeable">Can header mode be changed? + true + + + allow_header_table_mode + Allow table mode for headers? + true + + + use_default_headers + Use custom default headers? + true + + + default_headers + Set default headers + Example: "meta-title,meta-user" + + defaultHeaders: f.this; + + String trimmed = defaultHeaders.value?.replaceAll("\\s","") + if (defaultHeaders.value != trimmed) { + change defaultHeaders value { trimmed } + } + + + + show_more_menu + Show more menu for task item? + true + + + + + Zvoľte nový filter + Aktualizovať zobrazenie s vybraným filtrom + Skryté + Fulltext + Fulltext a rozšírené + Zoraďovanie + Vyhľadávanie + Upravovanie + Zjednotiť filter so základným filtrom? + Typ vyhľadávania úloh + Mód hlavičiek + Predvolený mód hlavičiek + Môže byť mód hlavičiek zmenený? + Povoliť tabuľkový mód pre hlavičky? + Použiť vlastné predvolené hlavičky? + Predvolené hlavičky + Napríklad: "meta-title,meta-user" + Zobrazovať menu pre úlohovú položku? + Nastavenie položky + Filter + Súčasný filter + Zobrazenie úloh + Všeobecné + + + Neue Filter auswählen + Versteckt + Einfacher Suchmodus + Sortieren + Suchen + Bearbeiten + Kopfzeilenmodus + Standardkopfzeilenmodus + Erlaube Änderung des Kopfzeilenmodus? + Erlaube Tabellenmodus? + Eigene Kopfzeilen verwenden? + Anzuzeigende Attributmenge auswählen + Beispiel: "meta-title,meta-user" + Filter + Aktueller Filter + Allgemein + Aktualisiere die Ansicht mit dem ausgewählten Filter + Einfacher und erweiterter Suchmodus + Mit dem Basisfilter kombinieren? + Suchmodus im Aufgabenansicht + "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen + Menüeintrageinstellungen + Aufgabenansicht + + + + initialize + 368 + 208 + + hourglass_empty + + + data_sync + 368 + 328 + + + + settings + 496 + 112 + + settings + + admin + + true + true + true + true + + + + form_title + 4 + grid + + view_header + + visible + + + 0 + 0 + 1 + 4 + + outline + + + + + filter_update + 4 + grid + Filter + + filter_autocomplete_selection + + editable + + + 0 + 0 + 1 + 3 + + outline + + + + update_filter + + visible + + + 3 + 0 + 1 + 1 + + standard + + + + selected_filter_preview + + visible + + + 0 + 1 + 1 + 4 + + standard + + + + + current_additional_filter + 4 + grid + + filter_header + + visible + + + 0 + 0 + 1 + 4 + + outline + + + + current_filter_preview + + visible + + + 0 + 1 + 1 + 4 + + standard + + + + merge_filters + + hidden + + + 0 + 2 + 1 + 1 + + standard + + + + remove_filter + + hidden + + + 1 + 2 + 1 + 1 + + standard + + + + + view_dataGroup + 4 + grid + + view_search_type + + editable + required + + + 0 + 0 + 1 + 3 + + outline + + + + show_more_menu + + editable + required + + + 3 + 0 + 1 + 1 + + outline + + + + + view_headers + 5 + grid + + is_header_mode_changeable + + editable + required + + trans: t.this, + isChangeable: f.is_header_mode_changeable, + mode: f.headers_mode, + defaultMode: f.headers_default_mode; + + make [mode, defaultMode], editable on trans when { isChangeable.value } + make [mode, defaultMode], required on trans when { isChangeable.value } + + make [mode, defaultMode], hidden on trans when { !isChangeable.value } + make [mode, defaultMode], optional on trans when { !isChangeable.value } + + + + 0 + 0 + 1 + 1 + 0 + + outline + + + + allow_header_table_mode + + editable + required + + + 1 + 0 + 1 + 1 + + outline + + + + headers_mode + + editable + required + + + 2 + 0 + 1 + 2 + + outline + + + + headers_default_mode + + editable + required + + + 4 + 0 + 1 + 1 + + outline + + + + use_default_headers + + editable + + trans: t.this, + use: f.use_default_headers, + headers: f.default_headers; + + make headers,editable on trans when { use.value } + make headers,visible on trans when { !use.value } + + + + 0 + 1 + 1 + 1 + 0 + + outline + + + + default_headers + + editable + + + 1 + 1 + 1 + 4 + 0 + + outline + + + + + + initialized + 496 + 208 + + 0 + false + + + uninitialized + 240 + 208 + + 1 + false + + + a1 + read + initialized + settings + 1 + + + a2 + regular + uninitialized + initialize + 1 + + + a3 + regular + initialize + initialized + 1 + + \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/preference_item.xml b/src/main/resources/petriNets/engine-processes/preference_item.xml deleted file mode 100644 index 6f2d35a2deb..00000000000 --- a/src/main/resources/petriNets/engine-processes/preference_item.xml +++ /dev/null @@ -1,2725 +0,0 @@ - - preference_item - PRI - Preference Item - check_box_outline_blank - true - false - false - - system - - true - true - true - - - - admin - - true - true - true - - - - default - - false - false - true - - - - - preference_item_delete - - - removeItemChildCases(useCase) - - - - - - system - System - - - admin - Admin - - - { com.netgrif.application.engine.workflow.domain.Case useCase -> - - def childCaseIds = useCase.dataSet['childItemIds'].value - if (childCaseIds == null || childCaseIds.isEmpty()) { - return - } - - removeChildItemFromParent(useCase.dataSet['parentId'].value[0], useCase) - - def childCases = workflowService.findAllById(childCaseIds) - async.run { - childCases.each { - workflowService.deleteCase(it) - } - } - } - - - { - com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete, - com.netgrif.application.engine.petrinet.domain.dataset.TaskField previewTaskRef, - com.netgrif.application.engine.petrinet.domain.dataset.CaseField selectedFilterRef, - com.netgrif.application.engine.petrinet.domain.dataset.ButtonField updateBtn, - com.netgrif.application.engine.petrinet.domain.Transition trans, - boolean taskTypeOnly - -> - if (filterAutocomplete.getOptions().containsKey(filterAutocomplete.value)) { - change previewTaskRef value { - return [findTask({it.caseId.eq(filterAutocomplete.value).and(it.transitionId.eq("view_filter"))}).stringId] - } - make updateBtn,editable on trans when { true } - } else { - change filterAutocomplete options { - def findAllPredicate - if (taskTypeOnly) { - findAllPredicate = { filterCase -> - !selectedFilterRef.value.contains(filterCase.stringId) && - filterCase.dataSet["filter_type"].value == "Task" - } - } else { - findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) } - } - return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") - .findAll(findAllPredicate) - .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) - } - change previewTaskRef value { [] } - make updateBtn,visible on trans when { true } - } - } - - - { - com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField toBeUpdated, - com.netgrif.application.engine.petrinet.domain.dataset.MultichoiceMapField valueSelector, - com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField optionsHolder - -> - def existingOptions = optionsHolder.options - def selectedValues = valueSelector.value - def newOptions = [:] - - if (selectedValues != null) { - existingOptions.each { key, value -> - if (selectedValues.contains(key)) { - newOptions.put(key, value) - } - } - } - - if (!newOptions.containsKey(toBeUpdated.value)) { - change toBeUpdated value { null } - } - - change toBeUpdated options { newOptions } - } - - - { - -> - String query = String.format("processIdentifier:preference_item AND uriNodeId:%s AND dataSet.is_auto_select.booleanValue:true AND NOT stringId:%s", - useCase.uriNodeId, useCase.stringId) - def itemCase = findCaseElastic(query) - if (itemCase == null) { - return - } - - setData("item_settings", itemCase, [ - "is_auto_select": [ - "value": false, - "type": "boolean" - ] - ]) - } - - - - parentId - - <allowedNets> - <allowedNet>preference_item</allowedNet> - </allowedNets> - </data> - <data type="text"> - <id>move_previous_dest_uri</id> - <title/> - </data> - <data type="multichoice_map"> - <id>move_dest_uri</id> - <title name="move_dest_uri">Destination URI - List of nodes representing destination URI - - autocomplete - - - moveDestUri: f.move_dest_uri; - - String uriNodeId = elasticCaseService.findUriNodeId(useCase) - def node = uriService.findById(uriNodeId) - updateMultichoiceWithCurrentNode(moveDestUri, node) - - - prevDestUri: f.move_previous_dest_uri, - moveDestUri: f.move_dest_uri; - - String newUri = moveDestUri.value.join("/") - newUri = newUri.replace("//","/") - - String corrected = getCorrectedUri(newUri) - - if (corrected == newUri) { - def node = uriService.findByUri(newUri) - change moveDestUri options { findOptionsBasedOnSelectedNode(node) } - } else { - change moveDestUri value { splitUriPath(corrected) } - } - - - - move_dest_uri_new_node - New node to be added - Enter new node name - - - move_add_node - - <placeholder name="move_add_node">Add</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - newNodeName: f.move_dest_uri_new_node, - selectedUri: f.move_dest_uri; - - if (newNodeName.value == null || newNodeName.value == "") { - return - } - - String prefixUri = selectedUri.value.join("/") - prefixUri = prefixUri.replace("//","/") - - String newUri = prefixUri + uriService.getUriSeparator() + newNodeName.value - def newNode = uriService.getOrCreate(newUri, com.netgrif.application.engine.petrinet.domain.UriContentType.CASE) - - change selectedUri value { splitUriPath(newNode.uriPath) } - - change newNodeName value { null } - </action> - </data> - <data type="i18n"> - <id>duplicate_new_title</id> - <title name="duplicate_new_title">Title of duplicated view - - - duplicate_view_identifier - View identifier - Must be unique - - - childItemIds - - <allowedNets> - <allowedNet>preference_item</allowedNet> - </allowedNets> - </data> - <data type="taskRef"> - <id>childItemForms</id> - <title/> - </data> - <data type="boolean" immediate="true"> - <id>hasChildren</id> - <title/> - </data> - <data type="button"> - <id>duplicate_reset_childItemIds</id> - <title/> - <action trigger="set"> - hasChildren: f.hasChildren, - childItemIds: f.childItemIds; - - change childItemIds value { [] } - change hasChildren value { false } - </action> - </data> - <data type="text" immediate="true"> - <id>menu_item_identifier</id> - <title name="menu_item_identifier">Menu item identifier - - - nodePath - Item URI - - 0 - - - nodePath: f.nodePath, - menu_item_identifier: f.menu_item_identifier; - - change menu_item_identifier value { - def idx = nodePath.value.lastIndexOf(uriService.getUriSeparator()) - return nodePath.value.substring(idx + 1) - } - - - - - - - - menu_icon - Menu icon identifier - Material icon identifier. List of icons with identifiers is available online. - - icon: f.this, - iconPreview: f.menu_icon_preview; - - changeCaseProperty "icon" about { icon.value; } - - if (icon.value == "") { - change iconPreview value {"""]]>} - return; - } - - change iconPreview value { - """]]> + icon.value + """]]> - } - - - - menu_icon_preview - Menu icon preview - - htmltextarea - - - - menu_name_as_visible - Name of the item - Is shown in the menu - - autocomplete - - - - menu_name - Name of the item - Will be shown in the menu - - menu_name_as_visible: f.menu_name_as_visible, - name: f.menu_name; - - changeCaseProperty "title" about { name.value } - change menu_name_as_visible choices { [name.value] } - change menu_name_as_visible value { name.value } - - - - tab_icon - Tab icon identifier - Material icon identifier. List of icons with identifiers is available online. - - icon: f.this, - iconPreview: f.tab_icon_preview; - - if (icon.value == "") { - change iconPreview value {"""]]>} - return; - } - - change iconPreview value { - """]]> + icon.value + """]]> - } - - - - tab_icon_preview - Tab icon preview - - htmltextarea - - - - use_tab_icon - Display tab icon? - true - - - tab_name - Name of the item - Will be shown in tab - - - add_allowed_roles - - <placeholder name="allow_roles">Allow view for roles</placeholder> - <action trigger="set"> - allowedRoles: f.allowed_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change allowedRoles options {return configurableMenuService.addSelectedRoles(allowedRoles, processesAvailable, rolesAvailable)} - - change rolesAvailable value {[]} - change rolesAvailable options {[:]} - change processesAvailable value {null} - </action> - </data> - <data type="button"> - <id>remove_allowed_roles</id> - <title/> - <placeholder name="remove_from_allowed_roles">Remove from allowed roles</placeholder> - <action trigger="set"> - allowedRoles: f.allowed_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change allowedRoles options {return configurableMenuService.removeSelectedRoles(allowedRoles)} - - change allowedRoles value {[]} - change rolesAvailable value {[]} - change rolesAvailable options {[:]} - change processesAvailable value {null} - </action> - </data> - <data type="button"> - <id>add_banned_roles</id> - <title/> - <placeholder name="ban_roles">Ban view for roles</placeholder> - <action trigger="set"> - bannedRoles: f.banned_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change bannedRoles options {return configurableMenuService.addSelectedRoles(bannedRoles, processesAvailable, rolesAvailable)} - - change rolesAvailable value {[]} - change rolesAvailable options {[:]} - change processesAvailable value {null} - </action> - </data> - <data type="button"> - <id>remove_banned_roles</id> - <title/> - <placeholder name="remove_from_banned_roles">Remove from banned roles</placeholder> - <action trigger="set"> - bannedRoles: f.banned_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change bannedRoles options { return configurableMenuService.removeSelectedRoles(bannedRoles) } - - change bannedRoles value { [] } - change rolesAvailable value { [] } - change rolesAvailable options { [:] } - change processesAvailable value { null } - </action> - </data> - <data type="enumeration_map" immediate="true"> - <id>processes_available</id> - <title name="available_processes">Your processes - Select a process containing roles you wish to add to allowed or banned roles lists. - - processes: f.this; - - change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } - - - processes: f.this, - allowedRoles: f.allowed_roles, - bannedRoles: f.banned_roles, - rolesAvailable: f.roles_available; - - if (processes.value != null) { - change rolesAvailable options { return configurableMenuService.getAvailableRolesFromNet(processes, allowedRoles, bannedRoles) } - } else { - change rolesAvailable options { [:] } - } - change rolesAvailable value { [] } - - - - roles_available - Available roles from selected process - - - allowed_roles - Allowed roles - List of roles allowed to view this menu entry. - - [:] - - - - banned_roles - Banned roles - List of roles not allowed to view this menu entry. - - [:] - - - - selected_filter_preview - - </data> - <data type="taskRef"> - <id>current_filter_preview</id> - <title/> - </data> - <data type="i18n"> - <id>filter_header</id> - <title/> - <init name="filter_header">Current filter</init> - <component> - <name>divider</name> - </component> - </data> - <data type="text"> - <id>new_filter_id</id> - <title/> - </data> - <data type="boolean" immediate="true"> - <id>contains_filter</id> - <title/> - <init>false</init> - </data> - <data type="enumeration_map"> - <id>filter_autocomplete_selection</id> - <title name="filter_autocomplete_selection">Select new filter - - autocomplete_dynamic - - - trans: t.item_settings, - useCustomView: f.use_custom_view, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans, false) - - make update_filter,hidden on trans when { useCustomView.value } - - - trans: t.item_settings, - useCustomView: f.use_custom_view, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans, false) - - make update_filter,hidden on trans when { useCustomView.value } - - - - update_filter - - <placeholder name="update_filter">Update view with selected filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - trans: t.item_settings, - update_filter: f.update_filter, - contains_filter: f.contains_filter, - filter_case: f.filter_case, - filterAutocomplete: f.filter_autocomplete_selection; - - - change contains_filter value { filterAutocomplete.value != null && filterAutocomplete.value != "" } - change filter_case value { [filterAutocomplete.value] } - change filterAutocomplete value { "" } - make update_filter,visible on trans when { true } - </action> - </data> - <data type="caseRef"> - <id>filter_case</id> - <title/> - <action trigger="set"> - additionalFilterCase: f.additional_filter_case, - additionalAutocomplete: f.additional_filter_autocomplete_selection, - additionalUpdate: f.update_additional_filter, - additionalFilterPreview: f.selected_additional_filter_preview, - mergeFilters: f.merge_filters, - filterHeader: f.filter_header, - currentAdditionalFilterPreview: f.current_additional_filter_preview, - taskSettingsTrans: t.task_view_settings, - settingsTrans: t.item_settings, - caseViewHeader: f.case_view_header, - caseViewSettingsTaskRef: f.case_view_settings_taskRef, - taskViewHeader: f.task_view_header, - taskViewSettingsTaskRef: f.task_view_settings_taskRef, - filterTaskRef: f.current_filter_preview, - filterCaseRef: f.filter_case; - - if (filterCaseRef.value == null || filterCaseRef.value == []) { - return - } - - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - change filterTaskRef value {return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId]} - - if (filterCase.dataSet["filter_type"].value == "Case") { - make caseViewHeader,editable on settingsTrans when { true } - make caseViewSettingsTaskRef,editable on settingsTrans when { true } - - make additionalAutocomplete,editable on taskSettingsTrans when { true } - make additionalUpdate,visible on taskSettingsTrans when { true } - make additionalFilterPreview,visible on taskSettingsTrans when { true } - make mergeFilters,visible on taskSettingsTrans when { true } - make filterHeader,visible on taskSettingsTrans when { true } - make currentAdditionalFilterPreview,visible on taskSettingsTrans when { true } - } else { - make caseViewHeader,hidden on settingsTrans when { true } - make caseViewSettingsTaskRef,hidden on settingsTrans when { true } - - make additionalAutocomplete,hidden on taskSettingsTrans when { true } - make additionalUpdate,hidden on taskSettingsTrans when { true } - make additionalFilterPreview,hidden on taskSettingsTrans when { true } - make mergeFilters,hidden on taskSettingsTrans when { true } - make filterHeader,hidden on taskSettingsTrans when { true } - make currentAdditionalFilterPreview,hidden on taskSettingsTrans when { true } - } - make taskViewHeader,editable on settingsTrans when { true } - make taskViewSettingsTaskRef,editable on settingsTrans when { true } - - change additionalFilterCase value { [] } - </action> - <allowedNets> - <allowedNet>filter</allowedNet> - </allowedNets> - </data> - <data type="boolean" immediate="true"> - <id>use_custom_view</id> - <title name="use_custom_view">Use custom view? - false - - - custom_view_selector - Custom view configuration selector - Example: "demo-tabbed-views" - - - is_auto_select - View auto selection - If selected, the view will be automatically opened - false - - is_auto_select: f.is_auto_select; - - if (!is_auto_select.value) - return - - removeAnyAutoSelectFlagInFolder() - - - - - - case_view_search_type - Search type for case view - - - - - - fulltext_advanced - - - create_case_button_title - "New case" button title - - - create_case_button_icon_preview - Icon preview - add]]> - - htmltextarea - - - - create_case_button_icon - "New case" button icon identifier - add - - create_case_button_icon_preview: f.create_case_button_icon_preview, - create_case_button_icon: f.create_case_button_icon; - - - if (create_case_button_icon.value == "") { - change create_case_button_icon_preview value {"""]]>} - return; - } - - change create_case_button_icon_preview value { - """]]> + create_case_button_icon.value + """]]> - } - - - - show_create_case_button - Show create case button? - true - - - case_require_title_in_creation - Require title input in case creation? - true - - - case_banned_nets_in_creation - Banned processes for creation - Write down process identifiers separated by comma. Example: mynet1,mynet2 - - bannedNets: f.this; - - String trimmed = bannedNets.value?.replaceAll("\\s","") - if (bannedNets.value != trimmed) { - change bannedNets value { trimmed } - } - - - - case_view_header - - <init name="case_view_header">Case view</init> - <component> - <name>divider</name> - </component> - </data> - <data type="taskRef"> - <id>case_view_settings_taskRef</id> - <title/> - <init>case_view_settings</init> - </data> - <data type="boolean" immediate="true"> - <id>case_show_more_menu</id> - <title name="case_show_more_menu">Show more menu for case item? - false - - - case_allow_header_table_mode - Allow table mode for headers? - true - - - case_headers_mode - Header mode - - - - - - sort,edit,search - - headersMode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode, - holder: f.case_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - headersMode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode, - holder: f.case_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - - case_headers_default_mode - Default header mode - - - - - - sort - - - case_headers_options_holder - - <options> - <option key="sort" name="sort">Sort</option> - <option key="search" name="search">Search</option> - <option key="edit" name="edit">Edit</option> - </options> - </data> - <data type="boolean" immediate="true"> - <id>case_is_header_mode_changeable</id> - <title name="is_header_mode_changeable">Can header mode be changed? - true - - - use_case_default_headers - Use custom default headers? - true - - - case_default_headers - Set default headers - Example: "meta-title,meta-visualId" - - defaultHeaders: f.this; - - String trimmed = defaultHeaders.value?.replaceAll("\\s","") - if (defaultHeaders.value != trimmed) { - change defaultHeaders value { trimmed } - } - - - - - - selected_additional_filter_preview - - </data> - <data type="taskRef"> - <id>current_additional_filter_preview</id> - <title/> - </data> - <data type="enumeration_map"> - <id>additional_filter_autocomplete_selection</id> - <title name="filter_autocomplete_selection">Select new filter - - autocomplete_dynamic - - - trans: t.task_view_settings, - filterAutocomplete: f.this, - filterCase: f.additional_filter_case, - updateFilter: f.update_additional_filter, - previewTaskRef: f.selected_additional_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filterCase, updateFilter, trans, true) - - - trans: t.task_view_settings, - filterAutocomplete: f.this, - filterCase: f.additional_filter_case, - updateFilter: f.update_additional_filter, - previewTaskRef: f.selected_additional_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filterCase, updateFilter, trans, true) - - - - update_additional_filter - - <placeholder name="update_filter">Update view with selected filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - trans: t.task_view_settings, - updateFilter: f.update_additional_filter, - filterCase: f.additional_filter_case, - filterAutocomplete: f.additional_filter_autocomplete_selection; - - change filterCase value { [filterAutocomplete.value] } - change filterAutocomplete value { "" } - make updateFilter,visible on trans when { true } - </action> - </data> - <data type="button"> - <id>remove_additional_filter</id> - <title/> - <placeholder name="remove_additional_filter">Remove additional filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - filterCase: f.additional_filter_case; - - change filterCase value { [] } - </action> - </data> - <data type="caseRef"> - <id>additional_filter_case</id> - <title/> - <action trigger="set"> - taskViewTrans: t.task_view_settings, - mergeFilters: f.merge_filters, - filterHeader: f.filter_header, - filterTaskRef: f.current_additional_filter_preview, - removeButton: f.remove_additional_filter, - filterCaseRef: f.additional_filter_case; - - if (filterCaseRef.value[0] == null) { - make mergeFilters,hidden on taskViewTrans when { true } - make filterHeader,hidden on taskViewTrans when { true } - make removeButton,hidden on taskViewTrans when { true } - change filterTaskRef value { [] } - return - } - - def filterCase = findCase({ it._id.eq(filterCaseRef.value[0]) }) - change filterTaskRef value { return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] } - make mergeFilters,editable on taskViewTrans when { true } - make filterHeader,visible on taskViewTrans when { true } - make removeButton,editable on taskViewTrans when { true } - </action> - <allowedNets> - <allowedNet>filter</allowedNet> - </allowedNets> - </data> - <data type="boolean" immediate="true"> - <id>merge_filters</id> - <title name="merge_filters">Merge with base filter? - true - - - task_view_settings_taskRef - - <init>task_view_settings</init> - </data> - <data type="i18n"> - <id>task_view_header</id> - <title/> - <init name="task_view_header">Task view</init> - <component> - <name>divider</name> - </component> - </data> - <data type="enumeration_map" immediate="true"> - <id>task_view_search_type</id> - <title name="task_view_search_type">Search type for task view - - - - - - fulltext_advanced - - - task_headers_mode - Header mode - - - - - sort,edit - - headersMode: f.task_headers_mode, - defaultMode: f.task_headers_default_mode, - holder: f.task_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - headersMode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode, - holder: f.case_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - - task_headers_default_mode - Default header mode - - - - - sort - - - task_headers_options_holder - - <options> - <option key="sort" name="sort">Sort</option> - <option key="edit" name="edit">Edit</option> - </options> - </data> - <data type="boolean" immediate="true"> - <id>task_is_header_mode_changeable</id> - <title name="is_header_mode_changeable">Can header mode be changed? - true - - - task_allow_header_table_mode - Allow table mode for headers? - true - - - use_task_default_headers - Use custom default headers? - true - - - task_default_headers - Set default headers - Example: "meta-title,meta-user" - - defaultHeaders: f.this; - - String trimmed = defaultHeaders.value?.replaceAll("\\s","") - if (defaultHeaders.value != trimmed) { - change defaultHeaders value { trimmed } - } - - - - task_show_more_menu - Show more menu for task item? - true - - - order_down - - <placeholder>south</placeholder> - <component> - <name>icon</name> - <property key="stretch">true</property> - </component> - <action trigger="set"> - parentId: f.parentId; - - def parentCase = workflowService.findOne(parentId.value[0]) - def taskId = useCase.tasks.find { it.transition == "row_for_ordering" }.task - def taskRefValue = parentCase.dataSet['childItemForms'].value - int taskRefValueSize = taskRefValue.size() - def caseRefValue = parentCase.dataSet['childItemIds'].value - int caseRefValueSize = caseRefValue.size() - - int idxInTaskRef = taskRefValue.indexOf(taskId) - if (idxInTaskRef < taskRefValueSize - 1) { - Collections.swap(taskRefValue, idxInTaskRef, idxInTaskRef + 1) - } - - int idxInCaseRef = caseRefValue.indexOf(useCase.stringId) - if (idxInCaseRef < caseRefValueSize - 1) { - Collections.swap(caseRefValue, idxInCaseRef, idxInCaseRef + 1) - } - - setData("children_order", parentCase, [ - "childItemForms" : [ - "value" : taskRefValue, - "type" : "taskRef" - ], - "childItemIds" : [ - "value" : caseRefValue, - "type" : "caseRef" - ] - ]) - </action> - </data> - <data type="button"> - <id>order_up</id> - <title/> - <placeholder>north</placeholder> - <component> - <name>icon</name> - <property key="stretch">true</property> - </component> - <action trigger="set"> - parentId: f.parentId; - - def parentCase = workflowService.findOne(parentId.value[0]) - def taskId = useCase.tasks.find { it.transition == "row_for_ordering" }.task - def taskRefValue = parentCase.dataSet['childItemForms'].value - def caseRefValue = parentCase.dataSet['childItemIds'].value - - int idxInTaskRef = taskRefValue.indexOf(taskId) - if (idxInTaskRef > 0) { - Collections.swap(taskRefValue, idxInTaskRef - 1, idxInTaskRef) - } else { - return - } - - int idxInCaseRef = caseRefValue.indexOf(useCase.stringId) - if (idxInCaseRef > 0) { - Collections.swap(caseRefValue, idxInCaseRef - 1, idxInCaseRef) - } else { - return - } - - setData("children_order", parentCase, [ - "childItemForms" : [ - "value" : taskRefValue, - "type" : "taskRef" - ], - "childItemIds" : [ - "value" : caseRefValue, - "type" : "caseRef" - ] - ]) - </action> - </data> - - <!-- I18NS --> - <i18n locale="sk"> - <i18nString name="icon_preview">Náhľad ikony</i18nString> - <i18nString name="icon_identifier">Identifikátor ikony</i18nString> - <i18nString name="icon_identifier_desc">Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online.</i18nString> - <i18nString name="allow_roles">Pridaj k povoleným roliam</i18nString> - <i18nString name="remove_from_allowed_roles">Odstráň z povolených rolí</i18nString> - <i18nString name="ban_roles">Pridaj k zakázaným roliam</i18nString> - <i18nString name="remove_from_banned_roles">Odstráň zo zakázaných rolí</i18nString> - <i18nString name="available_processes">Vaše procesy</i18nString> - <i18nString name="available_processes_desc">Vyberte proces obsahujúci roly ktoré chcete pridať do zoznamu povolených alebo zakázaných rolí.</i18nString> - <i18nString name="available_roles">Dostupné roly</i18nString> - <i18nString name="create_case_button_title">Názov tlačidla "Nová inštancia"</i18nString> - <i18nString name="create_case_button_icon">Identifikátor ikony tlačidla "Nová inštancia"</i18nString> - <i18nString name="create_case_button_icon_preview">Náhľad ikony</i18nString> - <i18nString name="default_headers">Predvolené hlavičky</i18nString> - <i18nString name="default_headers_desc">Napríklad: "meta-title,meta-visualId"</i18nString> - <i18nString name="filter_autocomplete_selection">Zvoľte nový filter</i18nString> - <i18nString name="move_dest_uri">Cieľové URI</i18nString> - <i18nString name="duplicate_new_title">Názov duplikovanej položky</i18nString> - <i18nString name="duplicate_view_identifier">Identifikátor duplikovanej položky</i18nString> - <i18nString name="duplicate_view_identifier_desc">Musí byť jedinečný</i18nString> - <i18nString name="name">Názov položky</i18nString> - <i18nString name="name_desc">Bude zobrazený v menu</i18nString> - <i18nString name="tab_icon">Identifikátor ikony v karte</i18nString> - <i18nString name="tab_icon_identifier_desc">Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online.</i18nString> - <i18nString name="display_tab_icon">Zobraziť ikonu v karte?</i18nString> - <i18nString name="tab_name">Názov položky</i18nString> - <i18nString name="tab_name_desc">Bude zobrazený v karte</i18nString> - <i18nString name="update_filter">Aktualizovať zobrazenie s vybraným filtrom</i18nString> - <i18nString name="use_custom_view">Použiť vlastné zobrazenie?</i18nString> - <i18nString name="custom_view_selector">Konfiguračný identifikátor vlastného zobrazenia</i18nString> - <i18nString name="custom_view_selector_desc">Napríklad: "demo-tabbed-views"</i18nString> - <i18nString name="case_view_search_type">Typ vyhľadávania prípadov</i18nString> - <i18nString name="hidden">Skryté</i18nString> - <i18nString name="fulltext">Fulltext</i18nString> - <i18nString name="fulltext_advanced">Fulltext a rozšírené</i18nString> - <i18nString name="case_require_title_in_creation">Vyžadovať názov inštancii pri vytváraní?</i18nString> - <i18nString name="case_banned_nets_in_creation">Zakázané siete pri vytváraní</i18nString> - <i18nString name="case_banned_nets_in_creation_desc">Uveďte identifikátory procesov oddelené čiarkou. Napríklad: mynet1,mynet2</i18nString> - <i18nString name="show_create_case_button">Zobraziť tlačidlo na vytvorenie prípadu?</i18nString> - <i18nString name="case_show_more_menu">Zobrazovať menu pre prípadovú položku?</i18nString> - <i18nString name="sort">Zoraďovanie</i18nString> - <i18nString name="search">Vyhľadávanie</i18nString> - <i18nString name="edit">Upravovanie</i18nString> - <i18nString name="merge_filters">Zjednotiť filter so základným filtrom?</i18nString> - <i18nString name="task_view_search_type">Typ vyhľadávania úloh</i18nString> - <i18nString name="headers_mode">Mód hlavičiek</i18nString> - <i18nString name="headers_default_mode">Predvolený mód hlavičiek</i18nString> - <i18nString name="is_header_mode_changeable">Môže byť mód hlavičiek zmenený?</i18nString> - <i18nString name="allow_header_table_mode">Povoliť tabuľkový mód pre hlavičky?</i18nString> - <i18nString name="use_default_headers">Použiť vlastné predvolené hlavičky?</i18nString> - <i18nString name="task_default_headers">Predvolené hlavičky</i18nString> - <i18nString name="task_default_headers_desc">Napríklad: "meta-title,meta-user"</i18nString> - <i18nString name="task_show_more_menu">Zobrazovať menu pre úlohovú položku?</i18nString> - <i18nString name="item_settings">Nastavenie položky</i18nString> - <i18nString name="roles_management_title">Roly</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> - <i18nString name="move_item">Presunúť položku</i18nString> - <i18nString name="move_item_finish">Presunúť</i18nString> - <i18nString name="duplicate_item">Duplikovať položku</i18nString> - <i18nString name="duplicate_item_finish">Duplikovať</i18nString> - <i18nString name="additional_filter_update">Dodatočný filter</i18nString> - <i18nString name="filter_header">Súčasný filter</i18nString> - <i18nString name="case_view_header">Zobrazenie prípadov</i18nString> - <i18nString name="task_view_header">Zobrazenie úloh</i18nString> - <i18nString name="roles_allowed">Povolené roly</i18nString> - <i18nString name="roles_allowed_desc">Zoznam povolených rolí, ktoré môžu zobraziť túto položku</i18nString> - <i18nString name="roles_banned">Zakázané roly</i18nString> - <i18nString name="roles_banned_desc">Zoznam zakázaných rolí, ktoré nemôžu zobraziť túto položku</i18nString> - <i18nString name="item_settings_general">Všeobecné</i18nString> - <i18nString name="menu_item_identifier">Identifikátor položky</i18nString> - <i18nString name="remove_additional_filter">Odstrániť dodatočný filter</i18nString> - <i18nString name="nodePath">URI položky</i18nString> - <i18nString name="move_dest_uri_new_node">Nový uzol</i18nString> - <i18nString name="move_dest_uri_new_node_desc">Uveďte názov uzlu, ktorý chcete pridať</i18nString> - <i18nString name="move_dest_uri_desc">Zoznam uzlov reprezentujúce cieľovú URI</i18nString> - <i18nString name="move_add_node">Pridať</i18nString> - <i18nString name="is_auto_select">Automatické zvolenie zobrazenia</i18nString> - <i18nString name="is_auto_select_desc">Po automatickom zvolení sa dané zobrazenie používateľovi otvorí</i18nString> - </i18n> - <i18n locale="de"> - <i18nString name="icon_preview">Ikonevorschau</i18nString> - <i18nString name="icon_identifier">Ikone ID</i18nString> - <i18nString name="icon_identifier_desc">Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar.</i18nString> - <i18nString name="allow_roles">Zu zulässigen Rollen hinzufügen</i18nString> - <i18nString name="remove_from_allowed_roles">Aus zulässigen Rollen entfernen</i18nString> - <i18nString name="ban_roles">Zu verbotenen Rollen hinzufügen</i18nString> - <i18nString name="remove_from_banned_roles">Aus verbotenen Rollen entfernen</i18nString> - <i18nString name="available_processes">Ihre Prozesse</i18nString> - <i18nString name="available_processes_desc">Wählen Sie einen Prozess mit Rollen aus, die Sie zu Listen mit zulässigen oder verbotenen Rollen hinzufügen möchten.</i18nString> - <i18nString name="available_roles">Verfügbare Rollen</i18nString> - <i18nString name="create_case_button_title">Schaltflächentitel "Neuer Fall"</i18nString> - <i18nString name="create_case_button_icon">Ikone ID</i18nString> - <i18nString name="create_case_button_icon_preview">Ikonevorschau</i18nString> - <i18nString name="default_headers">Anzuzeigende Attributmenge auswählen</i18nString> - <i18nString name="filter_autocomplete_selection">Neue Filter auswählen</i18nString> - <i18nString name="default_headers_desc">Beispiel: "meta-title,meta-visualId"</i18nString> - <i18nString name="tab_icon_identifier_desc">Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar.</i18nString> - <i18nString name="custom_view_selector_desc">Beispiel: "demo-tabbed-views"</i18nString> - <i18nString name="hidden">Versteckt</i18nString> - <i18nString name="fulltext">Einfacher Suchmodus</i18nString> - <i18nString name="sort">Sortieren</i18nString> - <i18nString name="search">Suchen</i18nString> - <i18nString name="edit">Bearbeiten</i18nString> - <i18nString name="headers_mode">Kopfzeilenmodus</i18nString> - <i18nString name="headers_default_mode">Standardkopfzeilenmodus</i18nString> - <i18nString name="is_header_mode_changeable">Erlaube Änderung des Kopfzeilenmodus?</i18nString> - <i18nString name="allow_header_table_mode">Erlaube Tabellenmodus?</i18nString> - <i18nString name="use_default_headers">Eigene Kopfzeilen verwenden?</i18nString> - <i18nString name="task_default_headers">Anzuzeigende Attributmenge auswählen</i18nString> - <i18nString name="task_default_headers_desc">Beispiel: "meta-title,meta-user"</i18nString> - <i18nString name="roles_management_title">Rollen</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> - <i18nString name="additional_filter_update">Zusätzlicher Filter</i18nString> - <i18nString name="filter_header">Aktueller Filter</i18nString> - <i18nString name="roles_allowed">Zulässige Rollen</i18nString> - <i18nString name="item_settings_general">Allgemein</i18nString> - <i18nString name="menu_item_identifier">Identifikationsnummer des Menüeintrages</i18nString> - <i18nString name="remove_additional_filter">Zusatzfilter entfernen</i18nString> - <i18nString name="nodePath">Menüeintrag-URI</i18nString> - <i18nString name="move_dest_uri_new_node">Neuer Knoten</i18nString> - <i18nString name="move_add_node">Hinzufügen</i18nString> - <i18nString name="move_dest_uri">Ziel URI</i18nString> - <i18nString name="duplicate_new_title">Titel der kopierten Ansicht</i18nString> - <i18nString name="duplicate_view_identifier">Identifikator der kopierten Ansicht</i18nString> - <i18nString name="duplicate_view_identifier_desc">Muss einzigartig sein</i18nString> - <i18nString name="name">Titel des Eintrages</i18nString> - <i18nString name="name_desc">Wird im Menü angezeigt</i18nString> - <i18nString name="tab_icon">Ikonen Identifikator der Registerkarte</i18nString> - <i18nString name="display_tab_icon">Zeige die Registerkarte Ikone an?</i18nString> - <i18nString name="tab_name">Titel der Registerkarte</i18nString> - <i18nString name="tab_name_desc">Wird in der Registerkarte angezeigt</i18nString> - <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> - <i18nString name="use_custom_view">Eigener Ansicht anwenden?</i18nString> - <i18nString name="custom_view_selector">Konfigurationsidentifikator der eigenen Ansicht</i18nString> - <i18nString name="case_view_search_type">Suchmodus im Fallansicht</i18nString> - <i18nString name="fulltext_advanced">Einfacher und erweiterter Suchmodus</i18nString> - <i18nString name="case_require_title_in_creation">Erforde den Titel beim erzeugen von Fällen?</i18nString> - <i18nString name="case_banned_nets_in_creation">Ausgeschlossene Prozesse</i18nString> - <i18nString name="case_banned_nets_in_creation_desc">Trenne die Prozessidentifikatoren mit einer Komma. z.B.: netz1,netz2</i18nString> - <i18nString name="show_create_case_button">Schaltfläche „Fall erstellen“ anzeigen?</i18nString> - <i18nString name="case_show_more_menu">"Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen</i18nString> - <i18nString name="merge_filters">Mit dem Basisfilter kombinieren?</i18nString> - <i18nString name="task_view_search_type">Suchmodus im Aufgabenansicht</i18nString> - <i18nString name="task_show_more_menu">"Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen</i18nString> - <i18nString name="item_settings">Menüeintrageinstellungen</i18nString> - <i18nString name="move_item">Menüeintrag verschieben</i18nString> - <i18nString name="move_item_finish">verschieben</i18nString> - <i18nString name="duplicate_item">Menüeintrag duplizieren</i18nString> - <i18nString name="duplicate_item_finish">duplizieren</i18nString> - <i18nString name="case_view_header">Fallansicht</i18nString> - <i18nString name="task_view_header">Aufgabenansicht</i18nString> - <i18nString name="roles_allowed_desc">Rollen mit Zugriff auf diesen Menüeintrag</i18nString> - <i18nString name="roles_banned">Verbotene Rollen</i18nString> - <i18nString name="roles_banned_desc">Rollen, für die wird den Menüeintrag ausgeblendet</i18nString> - <i18nString name="move_dest_uri_new_node_desc">Nächste URI-Teil angeben</i18nString> - <i18nString name="move_dest_uri_desc">Teile der Ziel URI</i18nString> - <i18nString name="is_auto_select">Automatische Anzeigeauswahl</i18nString> - <i18nString name="is_auto_select_desc">Wenn ausgewählt, wird die Ansicht automatisch geöffnet</i18nString> - </i18n> - - <!-- TRANSITIONS --> - <transition> - <id>initialize</id> - <x>340</x> - <y>220</y> - <label>initialize [await sync]</label> - <icon>hourglass_empty</icon> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - <view>true</view> - <cancel>true</cancel> - <assign>true</assign> - </logic> - </roleRef> - <dataGroup> - <id>view</id> - <dataRef> - <id>filter_case</id> - <logic> - <behavior>forbidden</behavior> - <action trigger="set"> - filterCaseRef: f.filter_case, - menu_name: f.menu_name; - - if (filterCaseRef.value == null || filterCaseRef.value == []) { - return - } - - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - if (!menu_name.value) { - change menu_name value {return filterCase.dataSet["i18n_filter_name"].value} - } - </action> - </logic> - </dataRef> - </dataGroup> - </transition> - - <transition> - <id>item_settings</id> - <x>460</x> - <y>100</y> - <label name="item_settings">Item settings</label> - <icon>settings</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - <view>true</view> - <cancel>true</cancel> - <assign>true</assign> - </logic> - </roleRef> - <dataGroup> - <id>pre_general</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>menu_item_identifier</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>nodePath</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>2</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <dataGroup> - <id>general_0</id> - <cols>4</cols> - <layout>grid</layout> - <title name="item_settings_general">General - - menu_name - - editable - - - 0 - 0 - 1 - 2 - - outline - - - - menu_icon - - editable - - - 2 - 0 - 1 - 1 - - outline - - - - menu_icon_preview - - visible - - - 3 - 0 - 1 - 1 - - standard - - - - tab_name - - editable - - - 0 - 1 - 1 - 1 - - outline - - - - use_tab_icon - - editable - - - 1 - 1 - 1 - 1 - 0 - - - - 0 - - - trans: t.this, - iconPreview: f.tab_icon_preview, - icon: f.tab_icon, - useIcon: f.use_tab_icon; - - make iconPreview,visible on trans when { useIcon.value } - make icon,editable on trans when { useIcon.value } - - make iconPreview,hidden on trans when { !useIcon.value } - make icon,hidden on trans when { !useIcon.value } - - - - - - tab_icon - - editable - - - 2 - 1 - 1 - 1 - - outline - - - - tab_icon_preview - - visible - - - 3 - 1 - 1 - 1 - - standard - - - - is_auto_select - - editable - - - 0 - 2 - 1 - 1 - - standard - - - - use_custom_view - - editable - - - 1 - 2 - 1 - 1 - - outline - - - 0 - - - trans: t.this, - caseHeader: f.case_view_header, - caseTaskRef: f.case_view_settings_taskRef, - taskHeader: f.task_view_header, - taskTaskRef: f.task_view_settings_taskRef, - - useTabIcon: f.use_tab_icon, - tabIconPreview: f.tab_icon_preview, - tabName: f.tab_name, - tabIcon: f.tab_icon, - filterSelection: f.filter_autocomplete_selection, - updateFilter: f.update_filter, - selectedFilterPreview: f.selected_filter_preview, - currentFilterHeader: f.filter_header, - currentFilterPreview: f.current_filter_preview, - - use: f.use_custom_view, - selector: f.custom_view_selector; - - make selector,editable on trans when { use.value } - make selector,visible on trans when { !use.value } - - make caseHeader,visible on trans when { !use.value } - make caseHeader,hidden on trans when { use.value } - make caseTaskRef,editable on trans when { !use.value } - make caseTaskRef,hidden on trans when { use.value } - - make taskHeader,visible on trans when { !use.value } - make taskHeader,hidden on trans when { use.value } - make taskTaskRef,editable on trans when { !use.value } - make taskTaskRef,hidden on trans when { use.value } - - make useTabIcon,editable on trans when { !use.value } - make useTabIcon,hidden on trans when { use.value } - make tabIconPreview,visible on trans when { !use.value } - make tabIconPreview,hidden on trans when { use.value } - make tabName,editable on trans when { !use.value } - make tabName,hidden on trans when { use.value } - make tabIcon,editable on trans when { !use.value } - make tabIcon,hidden on trans when { use.value } - make filterSelection,editable on trans when { !use.value } - make filterSelection,hidden on trans when { use.value } - make updateFilter,visible on trans when { !use.value } - make updateFilter,hidden on trans when { use.value } - make selectedFilterPreview,visible on trans when { !use.value } - make selectedFilterPreview,hidden on trans when { use.value } - make currentFilterHeader,visible on trans when { !use.value } - make currentFilterHeader,hidden on trans when { use.value } - make currentFilterPreview,visible on trans when { !use.value } - make currentFilterPreview,hidden on trans when { use.value } - - - - - - custom_view_selector - - visible - - - 2 - 2 - 1 - 2 - - outline - - - - - roles_management - 5 - grid - Roles - - processes_available - - editable - - - 0 - 0 - 2 - 1 - 0 - - outline - - - - roles_available - - editable - - - 1 - 0 - 2 - 1 - 0 - - outline - - - - add_allowed_roles - - editable - - - 2 - 0 - 1 - 1 - 0 - - - - - allowed_roles - - editable - - - 3 - 0 - 1 - 1 - 0 - - outline - - - - remove_allowed_roles - - editable - - - 4 - 0 - 1 - 1 - 0 - - - - - add_banned_roles - - editable - - - 2 - 1 - 1 - 1 - 0 - - - - - banned_roles - - editable - - - 3 - 1 - 1 - 1 - 0 - - outline - - - - remove_banned_roles - - editable - - - 4 - 1 - 1 - 1 - 0 - - - - - - filter_update - 4 - grid - Filter - - filter_autocomplete_selection - - editable - - - 0 - 0 - 1 - 3 - - outline - - - - update_filter - - visible - - - 3 - 0 - 1 - 1 - - standard - - - - selected_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - - current_filter - 4 - grid - - filter_header - - visible - - - 0 - 0 - 1 - 4 - - outline - - - - current_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - - case_view_settings_dataGroup - 4 - grid - - case_view_header - - hidden - - - 0 - 0 - 1 - 4 - - outline - - - - case_view_settings_taskRef - - hidden - - - 0 - 1 - 1 - 4 - - outline - - - - - task_view_settings_dataGroup - 4 - grid - - task_view_header - - hidden - - - 0 - 0 - 1 - 4 - - outline - - - - task_view_settings_taskRef - - hidden - - - 0 - 1 - 1 - 4 - - outline - - - - - - - move_item - 580 - 100 - - move_down - auto - - admin - - true - true - true - true - - - - move - 4 - grid - - move_dest_uri - - editable - required - - - 0 - 0 - 1 - 2 - - outline - - - - move_dest_uri_new_node - - editable - - - 2 - 0 - 1 - 1 - - outline - - - - move_add_node - - editable - - - 3 - 0 - 1 - 1 - - outline - - - - - finish - - - dest: f.move_dest_uri; - - if (dest.value == null || dest.value == []) { - throw new IllegalArgumentException("URI must not be empty!") - } - - String newUri = dest.value.join("/") - newUri = newUri.replace("//","/") - - changeMenuItem useCase uri { newUri } - - - Move - - - - - duplicate_item - 580 - 340 - - content_copy - auto - - admin - - true - true - true - true - - - - duplicate - 4 - grid - - duplicate_new_title - - editable - required - - - 0 - 0 - 1 - 4 - - outline - - - - duplicate_view_identifier - - editable - required - - - 0 - 1 - 1 - 4 - - outline - - - - - finish - - - identifier: f.duplicate_view_identifier, - title: f.duplicate_new_title; - - duplicateMenuItem(useCase, title.value, identifier.value) - - - Duplicate - - - - - change_filter - 460 - 340 - - - system - - true - - - - new_filter_id - - editable - required - - - set_event_0 - - - new_filter_id: f.new_filter_id, - contains_filter: f.contains_filter, - filterTaskRef: f.current_filter_preview, - filterCaseRef: f.filter_case; - - change filterCaseRef value { [new_filter_id.value] } - change contains_filter value { new_filter_id.value != null && new_filter_id.value != "" } - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - change filterTaskRef value {return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId]} - - - - - - - - case_view_settings - 340 - 100 - - - system - - true - - - - case_view_dataGroup - 4 - grid - - case_view_search_type - - editable - required - - - 0 - 0 - 1 - 2 - - outline - - - - show_create_case_button - - editable - required - - - 2 - 0 - 1 - 1 - - outline - - - - case_show_more_menu - - editable - required - - - 3 - 0 - 1 - 1 - - outline - - - - create_case_button_title - - editable - - - 0 - 1 - 1 - 1 - 0 - - outline - - - - create_case_button_icon - - editable - - - 1 - 1 - 1 - 1 - 0 - - outline - - - - create_case_button_icon_preview - - visible - - - 2 - 1 - 1 - 1 - 0 - - standard - - - - case_require_title_in_creation - - editable - - - 3 - 1 - 1 - 1 - 0 - - standard - - - - case_banned_nets_in_creation - - editable - - - 0 - 2 - 1 - 4 - 0 - - outline - - - - - case_view_headers - 5 - grid - - case_is_header_mode_changeable - - editable - required - - trans: t.this, - isChangeable: f.case_is_header_mode_changeable, - mode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode; - - make mode,editable on trans when { isChangeable.value } - make mode,required on trans when { isChangeable.value } - make defaultMode,editable on trans when { isChangeable.value } - make defaultMode,required on trans when { isChangeable.value } - - make mode,hidden on trans when { !isChangeable.value } - make mode,optional on trans when { !isChangeable.value } - make defaultMode,hidden on trans when { !isChangeable.value } - make defaultMode,optional on trans when { !isChangeable.value } - - - - 0 - 0 - 1 - 1 - 0 - - outline - - - - case_allow_header_table_mode - - editable - required - - - 1 - 0 - 1 - 1 - 0 - - outline - - - - case_headers_mode - - editable - required - - - 2 - 0 - 1 - 2 - 0 - - outline - - - - case_headers_default_mode - - editable - required - - - 4 - 0 - 1 - 1 - 0 - - outline - - - - use_case_default_headers - - editable - - trans: t.this, - use: f.use_case_default_headers, - headers: f.case_default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - - - - 0 - 1 - 1 - 1 - 0 - - outline - - - - case_default_headers - - editable - - - 1 - 1 - 1 - 4 - 0 - - outline - - - - - - - task_view_settings - 340 - 340 - - - system - - true - - - - task_view_dataGroup - 4 - grid - - task_view_search_type - - editable - required - - - 0 - 0 - 1 - 3 - - outline - - - - task_show_more_menu - - editable - required - - - 3 - 0 - 1 - 1 - - outline - - - - - task_view_headers - 5 - grid - - task_is_header_mode_changeable - - editable - required - - trans: t.this, - isChangeable: f.task_is_header_mode_changeable, - mode: f.task_headers_mode, - defaultMode: f.task_headers_default_mode; - - make mode,editable on trans when { isChangeable.value } - make mode,required on trans when { isChangeable.value } - make defaultMode,editable on trans when { isChangeable.value } - make defaultMode,required on trans when { isChangeable.value } - - make mode,hidden on trans when { !isChangeable.value } - make mode,optional on trans when { !isChangeable.value } - make defaultMode,hidden on trans when { !isChangeable.value } - make defaultMode,optional on trans when { !isChangeable.value } - - - - 0 - 0 - 1 - 1 - 0 - - outline - - - - task_allow_header_table_mode - - editable - required - - - 1 - 0 - 1 - 1 - - outline - - - - task_headers_mode - - editable - required - - - 2 - 0 - 1 - 2 - - outline - - - - task_headers_default_mode - - editable - required - - - 4 - 0 - 1 - 1 - - outline - - - - use_task_default_headers - - editable - - trans: t.this, - use: f.use_task_default_headers, - headers: f.task_default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - - - - 0 - 1 - 1 - 1 - 0 - - outline - - - - task_default_headers - - editable - - - 1 - 1 - 1 - 4 - 0 - - outline - - - - - additional_filter_update - 4 - grid - Additional filter - - additional_filter_autocomplete_selection - - editable - - - 0 - 0 - 1 - 3 - - outline - - - - update_additional_filter - - editable - - - 3 - 0 - 1 - 1 - - standard - - - - selected_additional_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - - current_additional_filter - 4 - grid - - filter_header - - hidden - - - 0 - 0 - 1 - 4 - - outline - - - - current_additional_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - merge_filters - - hidden - - - 0 - 2 - 1 - 1 - - standard - - - - remove_additional_filter - - hidden - - - 1 - 2 - 1 - 1 - - standard - - - - - - children_order - 580 - 220 - - low_priority - auto - - admin - - true - true - true - true - - - - children_order_0 - 4 - grid - - childItemForms - - editable - - forms: f.childItemForms, - ids: f.childItemIds; - - def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } - change forms value { orderedTaskIds } - - - - 0 - 0 - 1 - 4 - - outline - - - - - - row_for_ordering - 741 - 219 - - - system - - true - true - true - true - - - - row_for_ordering_0 - 6 - grid - - menu_item_identifier - - visible - - - 0 - 0 - 1 - 2 - - outline - - - - menu_name_as_visible - - visible - - - 2 - 0 - 1 - 2 - - outline - - - - order_down - - editable - - - 4 - 0 - 1 - 1 - 1 - - outline - - - - order_up - - editable - - - 5 - 0 - 1 - 1 - 1 - - outline - - - - - finish - - - - delegate - - - - - - - uninitialized - 220 - 220 - - 1 - false - - - initialized - 460 - 220 - - 0 - false - - - - - a1 - regular - uninitialized - initialize - 1 - - - a7 - read - initialized - item_settings - 1 - - - a8 - regular - initialize - initialized - 1 - - - a9 - read - initialized - move_item - 1 - - - a10 - read - initialized - duplicate_item - 1 - - - a12 - read - initialized - change_filter - 1 - - - a13 - read - initialized - children_order - 1 - - \ No newline at end of file diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index 1422efaebf9..fa983411ebe 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -5,6 +5,11 @@ import com.netgrif.application.engine.TestHelper import com.netgrif.application.engine.auth.service.interfaces.IUserService import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest +import com.netgrif.application.engine.menu.domain.MenuItemConstants +import com.netgrif.application.engine.menu.domain.MenuItemView +import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants +import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewConstants +import com.netgrif.application.engine.menu.utils.MenuItemUtils import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.petrinet.domain.I18nString import com.netgrif.application.engine.petrinet.domain.UriContentType @@ -17,7 +22,6 @@ import com.netgrif.application.engine.workflow.domain.QCase import com.netgrif.application.engine.workflow.service.interfaces.IDataService import com.netgrif.application.engine.workflow.service.interfaces.ITaskService import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService -import com.netgrif.application.engine.workflow.domain.menu.MenuItemConstants import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -82,44 +86,64 @@ class MenuItemApiTest { Thread.sleep(4000) UriNode leafNode = uriService.findByUri("/netgrif/test/new_menu_item") + assert leafNode != null assert item.uriNodeId == uriService.findByUri("/netgrif/test").stringId - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON.attributeId].value == "device_hub" - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId].value == new I18nString("FILTER") - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId].value.toString() == "new_menu_item" - assert (item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_FILTER_CASE.attributeId].value as List)[0] == filter.stringId - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId].options.containsKey("role_1:filter_api_test") - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_ROLES.attributeId].options.containsKey("role_2:filter_api_test") - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS.attributeId].value == "meta-title,meta-title" - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS.attributeId].value == "meta-title,meta-title" + assert item.dataSet[MenuItemConstants.FIELD_MENU_ICON].value == "device_hub" + assert item.dataSet[MenuItemConstants.FIELD_MENU_NAME].value == new I18nString("FILTER") + assert item.dataSet[MenuItemConstants.FIELD_IDENTIFIER].value.toString() == "new_menu_item" + assert item.dataSet[MenuItemConstants.FIELD_BANNED_ROLES].options.containsKey("role_2:filter_api_test") + assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.containsKey("role_1:filter_api_test") + assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier assert filter.dataSet["filter"].filterMetadata["filterType"] == "Case" - assert filter.dataSet["filter"].allowedNets == ["filter", "preference_item"] - assert filter.dataSet["filter"].value == "processIdentifier:filter OR processIdentifier:preference_item" + assert filter.dataSet["filter"].allowedNets == ["filter", "menu_item"] + assert filter.dataSet["filter"].value == "processIdentifier:filter OR processIdentifier:menu_item" assert filter.dataSet["filter_type"].value == "Case" - assert leafNode != null - Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"/netgrif/test\"", PageRequest.of(0, 1))[0] - Case netgrifFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"/netgrif\"", PageRequest.of(0, 1))[0] + String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) + assert tabbedCaseViewId != null + Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + assert tabbedTaskViewId != null + Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) + assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false + assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] + assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" + + Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif/test\"", PageRequest.of(0, 1))[0] + Case netgrifFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif\"", PageRequest.of(0, 1))[0] UriNode testNode = uriService.findByUri("/netgrif") UriNode netgrifNode = uriService.getRoot() - Case rootFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue.keyword:\"/\"", PageRequest.of(0, 1))[0] + Case rootFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/\"", PageRequest.of(0, 1))[0] assert testFolder != null && testNode != null assert testFolder.uriNodeId == testNode.stringId - assert testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [netgrifFolder.stringId] - assert (testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(item.stringId) - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [testFolder.stringId] + assert testFolder.dataSet[MenuItemConstants.FIELD_PARENT_ID].value == [netgrifFolder.stringId] + assert (testFolder.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList).contains(item.stringId) + assert item.dataSet[MenuItemConstants.FIELD_PARENT_ID].value == [testFolder.stringId] assert netgrifFolder.uriNodeId == netgrifNode.stringId - assert netgrifFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [rootFolder.stringId] - assert (netgrifFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(testFolder.stringId) - assert rootFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [] - assert (rootFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(netgrifFolder.stringId) + assert netgrifFolder.dataSet[MenuItemConstants.FIELD_PARENT_ID].value == [rootFolder.stringId] + assert (netgrifFolder.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList).contains(testFolder.stringId) + assert rootFolder.dataSet[MenuItemConstants.FIELD_PARENT_ID].value == [] + assert (rootFolder.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList).contains(netgrifFolder.stringId) } @Test void testChangeFilterAndMenuItems() { Case caze = createMenuItem() Thread.sleep(3000) + + Case item = getMenuItem(caze) + String tabbedCaseViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) + Case tabbedCaseViewBeforeChange = workflowService.findOne(tabbedCaseViewIdBeforeChange) + String tabbedTaskViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseViewBeforeChange, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + def newUri = uriService.getOrCreate("/netgrif/test_new", UriContentType.DEFAULT) caze = setData(caze, [ "uri": newUri.uriPath, @@ -130,18 +154,33 @@ class MenuItemApiTest { "icon": "", "change_filter_and_menu": "0" ]) - Case item = getMenuItem(caze) + item = getMenuItem(caze) Case filter = getFilter(caze) - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId].value.toString() == "CHANGED FILTER" - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_ALLOWED_ROLES.attributeId].options.entrySet()[0].key.contains("role_2") - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS.attributeId].value == "meta-title,meta-title,meta-title" - assert item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS.attributeId].value == "meta-title,meta-title,meta-title" + assert item.dataSet[MenuItemConstants.FIELD_MENU_NAME].value.toString() == "CHANGED FILTER" + assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.entrySet()[0].key.contains("role_2") + assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier assert item.uriNodeId == newUri.stringId assert filter.dataSet["filter"].allowedNets == ["filter"] assert filter.dataSet["filter"].filterMetadata["defaultSearchCategories"] == false assert filter.dataSet["filter"].value == "processIdentifier:filter" + + String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) + assert tabbedCaseViewId != null && tabbedCaseViewId.equals(tabbedCaseViewIdBeforeChange) + Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) + Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) + assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false + assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] + assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" } @Test @@ -163,7 +202,6 @@ class MenuItemApiTest { apiCase = createMenuItem("/netgrif2/test2", "new_menu_item2") String viewId2 = apiCase.dataSet["menu_stringId"].value - // move view Thread.sleep(2000) apiCase = setData(apiCase, [ @@ -177,13 +215,12 @@ class MenuItemApiTest { Thread.sleep(2000) UriNode node = uriService.findByUri("/netgrif2") - Case folderCase = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif2\"", PageRequest.of(0, 1))[0] + Case folderCase = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif2\"", PageRequest.of(0, 1))[0] assert viewCase.uriNodeId == node.stringId - ArrayList childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList + ArrayList childIds = folderCase.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList assert childIds.contains(viewId) && childIds.size() == 2 - // cyclic move assertThrows(IllegalArgumentException.class, () -> { setData(apiCase, [ @@ -194,7 +231,6 @@ class MenuItemApiTest { ]) }) - // move folder setData(apiCase, [ "move_dest_uri": "/netgrif/test3", @@ -204,23 +240,23 @@ class MenuItemApiTest { ]) Thread.sleep(2000) - folderCase = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif/test3\"", PageRequest.of(0, 1))[0] - Case folderCase2 = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif\"", PageRequest.of(0, 1))[0] - assert folderCase != null && folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value == [folderCase2.stringId] + folderCase = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif/test3\"", PageRequest.of(0, 1))[0] + Case folderCase2 = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif\"", PageRequest.of(0, 1))[0] + assert folderCase != null && folderCase.dataSet[MenuItemConstants.FIELD_PARENT_ID].value == [folderCase2.stringId] - folderCase = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif/test3/netgrif2\"", PageRequest.of(0, 1))[0] + folderCase = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif/test3/netgrif2\"", PageRequest.of(0, 1))[0] assert folderCase != null node = uriService.findByUri("/netgrif/test3") assert node != null assert folderCase.uriNodeId == node.stringId - assert folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value == "/netgrif/test3/netgrif2" + assert folderCase.dataSet[MenuItemConstants.FIELD_NODE_PATH].value == "/netgrif/test3/netgrif2" - childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList + childIds = folderCase.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList assert childIds.size() == 2 folderCase = workflowService.findOne(childIds[0]) node = uriService.findByUri("/netgrif/test3/netgrif2") - assert folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value == "/netgrif/test3/netgrif2/test2" + assert folderCase.dataSet[MenuItemConstants.FIELD_NODE_PATH].value == "/netgrif/test3/netgrif2/test2" assert folderCase.uriNodeId == node.stringId viewCase = workflowService.findOne(viewId2) @@ -234,7 +270,7 @@ class MenuItemApiTest { Case apiCase = createMenuItem(starterUri, "new_menu_item") String itemId = apiCase.dataSet["menu_stringId"].value Case origin = workflowService.findOne(itemId) - Case testFolder = workflowService.findOne((origin.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList)[0]) + Case testFolder = workflowService.findOne((origin.dataSet[MenuItemConstants.FIELD_PARENT_ID].value as ArrayList)[0]) String newTitle = "New title" String newIdentifier = "new_identifier" @@ -243,39 +279,40 @@ class MenuItemApiTest { taskService.assignTask(duplicateTaskId) assertThrows(IllegalArgumentException.class, () -> { - testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId].value = new I18nString("") - testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId].value = newIdentifier + testFolder.dataSet[MenuItemConstants.FIELD_DUPLICATE_TITLE].value = new I18nString("") + testFolder.dataSet[MenuItemConstants.FIELD_DUPLICATE_IDENTIFIER].value = newIdentifier testFolder = workflowService.save(testFolder) taskService.finishTask(duplicateTaskId) }) assertThrows(IllegalArgumentException.class, () -> { - testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId].value = new I18nString(newTitle) - testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId].value = "new_menu_item" + testFolder.dataSet[MenuItemConstants.FIELD_DUPLICATE_TITLE].value = new I18nString(newTitle) + testFolder.dataSet[MenuItemConstants.FIELD_DUPLICATE_IDENTIFIER].value = "new_menu_item" testFolder = workflowService.save(testFolder) taskService.finishTask(duplicateTaskId) }) - testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId].value = new I18nString(newTitle) - testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId].value = newIdentifier + testFolder.dataSet[MenuItemConstants.FIELD_DUPLICATE_TITLE].value = new I18nString(newTitle) + testFolder.dataSet[MenuItemConstants.FIELD_DUPLICATE_IDENTIFIER].value = newIdentifier testFolder = workflowService.save(testFolder) taskService.finishTask(duplicateTaskId) - Case duplicated = workflowService.searchOne(QCase.case$.processIdentifier.eq("preference_item").and(QCase.case$.dataSet.get(MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId).value.eq(newIdentifier))) + Case duplicated = workflowService.searchOne(QCase.case$.processIdentifier.eq("menu_item") + .and(QCase.case$.dataSet.get(MenuItemConstants.FIELD_IDENTIFIER).value.eq(newIdentifier))) assert duplicated != null UriNode leafNode = uriService.findByUri("/netgrif/" + newIdentifier) assert duplicated.uriNodeId == testFolder.uriNodeId assert leafNode != null - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE.attributeId].value == null - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER.attributeId].value == null + assert duplicated.dataSet[MenuItemConstants.FIELD_DUPLICATE_TITLE].value == new I18nString("") + assert duplicated.dataSet[MenuItemConstants.FIELD_DUPLICATE_IDENTIFIER].value == "" assert duplicated.title == newTitle - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME.attributeId].value == new I18nString(newTitle) - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER.attributeId].value == newIdentifier - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId].value == "/netgrif/" + newIdentifier - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value == [] - assert duplicated.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_HAS_CHILDREN.attributeId].value == false + assert duplicated.dataSet[MenuItemConstants.FIELD_MENU_NAME].value == new I18nString(newTitle) + assert duplicated.dataSet[MenuItemConstants.FIELD_IDENTIFIER].value == newIdentifier + assert duplicated.dataSet[MenuItemConstants.FIELD_NODE_PATH].value == "/netgrif/" + newIdentifier + assert duplicated.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value == [] + assert duplicated.dataSet[MenuItemConstants.FIELD_HAS_CHILDREN].value == false assert duplicated.activePlaces["initialized"] == 1 } @@ -291,8 +328,8 @@ class MenuItemApiTest { caze = setData(caze, [ "uri": uri, "title": "FILTER", - "allowed_nets": "filter,preference_item", - "query": "processIdentifier:filter OR processIdentifier:preference_item", + "allowed_nets": "filter,menu_item", + "query": "processIdentifier:filter OR processIdentifier:menu_item", "type": "Case", "identifier": identifier, "icon": "device_hub", @@ -305,25 +342,37 @@ class MenuItemApiTest { void testRemoveMenuItem() { String starterUri = "/netgrif/test" Case apiCase = createMenuItem(starterUri, "new_menu_item") - String leafItemId = apiCase.dataSet["menu_stringId"].value + Case leafItemCase = getMenuItem(apiCase) - Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.${MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH.attributeId}.textValue:\"/netgrif/test\"", PageRequest.of(0, 1))[0] - String netgrifFolderId = (testFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList)[0] + sleep(2000) + Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif/test\"", PageRequest.of(0, 1))[0] + String netgrifFolderId = (testFolder.dataSet[MenuItemConstants.FIELD_PARENT_ID].value as ArrayList)[0] Case netgrifFolder = workflowService.findOne(netgrifFolderId) - assert (netgrifFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(testFolder.stringId) + assert (netgrifFolder.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList).contains(testFolder.stringId) assert workflowService.findOne(testFolder.stringId) != null - assert workflowService.findOne(leafItemId) != null + assert workflowService.findOne(leafItemCase.stringId) != null + String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(leafItemCase, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) + assert tabbedCaseViewId != null + Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + assert tabbedTaskViewId != null workflowService.deleteCase(testFolder) sleep(2000) netgrifFolder = workflowService.findOne(netgrifFolderId) - assert !(netgrifFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList).contains(testFolder.stringId) + assert !(netgrifFolder.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList).contains(testFolder.stringId) assertThrows(IllegalArgumentException.class, () -> { workflowService.findOne(testFolder.stringId) }) assertThrows(IllegalArgumentException.class, () -> { - workflowService.findOne(leafItemId) + workflowService.findOne(leafItemCase.stringId) + }) + assertThrows(IllegalArgumentException.class, () -> { + workflowService.findOne(tabbedCaseViewId) + }) + assertThrows(IllegalArgumentException.class, () -> { + workflowService.findOne(tabbedTaskViewId) }) } diff --git a/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy b/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy index cf6e0108859..940fa30b79e 100644 --- a/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy @@ -15,7 +15,7 @@ import com.netgrif.application.engine.workflow.domain.QCase import com.netgrif.application.engine.workflow.domain.QTask import com.netgrif.application.engine.workflow.domain.Task import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.SetDataEventOutcome -import com.netgrif.application.engine.workflow.domain.menu.MenuAndFilters +import com.netgrif.application.engine.menu.domain.MenuAndFilters import com.netgrif.application.engine.workflow.domain.repositories.CaseRepository import com.netgrif.application.engine.workflow.service.UserFilterSearchService import com.netgrif.application.engine.workflow.service.interfaces.IDataService diff --git a/src/test/resources/petriNets/filter_api_test.xml b/src/test/resources/petriNets/filter_api_test.xml index 5bb13019442..7f537c2253f 100644 --- a/src/test/resources/petriNets/filter_api_test.xml +++ b/src/test/resources/petriNets/filter_api_test.xml @@ -107,31 +107,20 @@ title: f.title, allowed_nets: f.allowed_nets, query: f.query, - group: f.group, identifier: f.identifier, icon: f.icon; - def item = findMenuItem(identifier.value) - def filter = getFilterFromMenuItem(item) - - changeFilter filter query { query.value } - changeFilter filter allowedNets { allowed_nets.value.split(",") as List } - changeFilter filter icon { icon.value } - changeFilter filter title { title.value } - changeFilter filter filterMetadata { [ + def metadata = [ "searchCategories" : [], "predicateMetadata" : [], "filterType" : type, "defaultSearchCategories": false, "inheritAllowedNets" : false - ] } - - changeMenuItem item filter { filter } - changeMenuItem item allowedRoles { ["role_2": "filter_api_test"] } - changeMenuItem item uri { uri.value } - changeMenuItem item title { title.value } - changeMenuItem item caseDefaultHeaders { "meta-title,meta-title,meta-title" } - changeMenuItem item taskDefaultHeaders { "meta-title,meta-title,meta-title" } + ] + awd() + createOrUpdateMenuItemAndFilter(uri.value, identifier.value, title.value, query.value, "Case", + "private", allowed_nets.value.split(",") as List, icon.value, ["role_2": "filter_api_test"], + [:], ["meta-title","meta-title","meta-title"], ["meta-title","meta-title","meta-title"], metadata) From 12c63795d513a7dd3efd6a31a78b070e52d32e79 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 10 Feb 2025 10:51:03 +0100 Subject: [PATCH 007/174] [NAE-2051] Implementovat moznost volby komponentu zobrazenia v menu itemoch - add logging to MenuItemService --- .../engine/menu/services/MenuItemService.java | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index 46115a02bba..3f72a466199 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -53,7 +53,9 @@ public Case createFilter(FilterBody body) throws TransitionNotExecutableExceptio filterCase.setIcon(body.getIcon()); filterCase = workflowService.save(filterCase); ToDataSetOutcome dataSetOutcome = body.toDataSet(); - return setDataWithExecute(filterCase, DefaultFiltersRunner.AUTO_CREATE_TRANSITION, dataSetOutcome.getDataSet()); + filterCase = setDataWithExecute(filterCase, DefaultFiltersRunner.AUTO_CREATE_TRANSITION, dataSetOutcome.getDataSet()); + log.trace("Created filter case [{}][{}]", filterCase.getStringId(), body.getTitle().getDefaultValue()); + return filterCase; } @Override @@ -61,11 +63,14 @@ public Case updateFilter(Case filterCase, FilterBody body) { filterCase.setIcon(body.getIcon()); filterCase = workflowService.save(filterCase); ToDataSetOutcome dataSetOutcome = body.toDataSet(); - return setData(filterCase, DefaultFiltersRunner.DETAILS_TRANSITION, dataSetOutcome.getDataSet()); + filterCase = setData(filterCase, DefaultFiltersRunner.DETAILS_TRANSITION, dataSetOutcome.getDataSet()); + log.trace("Updated filter case [{}][{}]", filterCase.getStringId(), body.getTitle().getDefaultValue()); + return filterCase; } @Override public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableException { + log.debug("Creation of menu item case with identifier [{}] started.", body.getIdentifier()); IUser loggedUser = userService.getLoggedOrSystem(); String sanitizedIdentifier = MenuItemUtils.sanitize(body.getIdentifier()); @@ -93,11 +98,14 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce viewCase = createView(body.getView()); } ToDataSetOutcome dataSetOutcome = body.toDataSet(parentItemCase.getStringId(), nodePath, viewCase); - return setDataWithExecute(menuItemCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + menuItemCase = setDataWithExecute(menuItemCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + log.debug("Created menu item case [{}] with identifier [{}].", menuItemCase.getStringId(), body.getIdentifier()); + return menuItemCase; } @Override public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNotExecutableException { + log.debug("Update of menu item case with identifier [{}] started.", body.getIdentifier()); String actualUriNodeId = uriService.findByUri(body.getUri()).getStringId(); if (!itemCase.getUriNodeId().equals(actualUriNodeId)) { itemCase.setUriNodeId(actualUriNodeId); @@ -107,7 +115,9 @@ public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNo Case viewCase = findView(itemCase); viewCase = handleView(viewCase, body.getView()); ToDataSetOutcome dataSetOutcome = body.toDataSet(viewCase); - return setData(itemCase, MenuItemConstants.TRANS_SYNC_ID, dataSetOutcome.getDataSet()); + itemCase = setData(itemCase, MenuItemConstants.TRANS_SYNC_ID, dataSetOutcome.getDataSet()); + log.debug("Updated menu item case [{}] with identifier [{}].", itemCase.getStringId(), body.getIdentifier()); + return itemCase; } @Override @@ -124,6 +134,8 @@ public Case createOrUpdateMenuItem(MenuItemBody body) throws TransitionNotExecut public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecutableException { Case itemCase = findMenuItem(body.getIdentifier()); if (itemCase != null) { + log.debug("Ignored creation or update of menu item case [{}] with identifier [{}].", itemCase.getStringId(), + body.getIdentifier()); return itemCase; } else { return createMenuItem(body); @@ -165,6 +177,7 @@ public boolean existsMenuItem(String identifier) { @Override public void moveItem(Case itemCase, String destUri) throws TransitionNotExecutableException { + log.debug("Move of menu item case [{}] started. Destination path [{}]", itemCase.getStringId(), destUri); if (MenuItemUtils.isCyclicNodePath(itemCase, destUri)) { throw new IllegalArgumentException(String.format("Cyclic path not supported. Destination path: %s", destUri)); } @@ -200,10 +213,12 @@ public void moveItem(Case itemCase, String destUri) throws TransitionNotExecutab workflowService.save(useCase); } } + log.debug("Moved menu item case [{}]. Destination path was [{}]", itemCase.getStringId(), destUri); } @Override public Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException { + log.debug("Duplication of menu item case [{}] started.", originItem.getStringId()); if (newIdentifier == null || newIdentifier.isEmpty()) { throw new IllegalArgumentException("View item identifier is null or empty!"); } @@ -258,6 +273,8 @@ public Case duplicateItem(Case originItem, I18nString newTitle, String newIdenti Case parent = workflowService.findOne(parentIdAsList.get(0)); appendChildCaseIdAndSave(parent, duplicated.getStringId()); } + log.debug("Duplicated menu item case [{}]. New title [{}] and new identifier [{}].", originItem.getStringId(), + newTitle.getDefaultValue(), newIdentifier); return workflowService.findOne(duplicated.getStringId()); } @@ -346,7 +363,11 @@ protected Case createView(ViewBody body) throws TransitionNotExecutableException } } ToDataSetOutcome dataSetOutcome = body.toDataSet(associatedViewCase, filterCase); - return setDataWithExecute(viewCase, ViewConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + viewCase = setDataWithExecute(viewCase, ViewConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + + log.trace("Created configuration view case [{}] of identifier [{}]", viewCase.getStringId(), + body.getViewProcessIdentifier()); + return viewCase; } protected Case updateView(Case viewCase, ViewBody body) throws TransitionNotExecutableException { @@ -357,11 +378,16 @@ protected Case updateView(Case viewCase, ViewBody body) throws TransitionNotExec associatedViewCase = handleView(associatedViewCase, body.getAssociatedViewBody()); ToDataSetOutcome outcome = body.toDataSet(associatedViewCase, filterCase); - return setData(viewCase, ViewConstants.TRANS_SYNC_ID, outcome.getDataSet()); + viewCase = setData(viewCase, ViewConstants.TRANS_SYNC_ID, outcome.getDataSet()); + + log.trace("Updated configuration view case [{}] of identifier [{}]", viewCase.getStringId(), + body.getViewProcessIdentifier()); + return viewCase; } protected void removeView(Case viewCase) { workflowService.deleteCase(viewCase); + log.trace("Removed configuration view case [{}].", viewCase.getStringId()); } protected Case handleFilter(Case filterCase, FilterBody body) throws TransitionNotExecutableException { @@ -476,6 +502,7 @@ protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case } folderCase = setDataWithExecute(folderCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); + log.trace("Created folder menu item [{}] with identifier [{}]", folderCase.getStringId(), body.getIdentifier()); return folderCase; } From 860b0d0a07f44c57218876385171aeb0259f9c98 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 10 Feb 2025 20:25:41 +0100 Subject: [PATCH 008/174] [NAE-2051] Implementovat moznost volby komponentu zobrazenia v menu itemoch - add or update javadoc --- .../logic/action/ActionDelegate.groovy | 12 +- .../engine/menu/domain/FilterBody.java | 11 +- .../engine/menu/domain/MenuItemBody.java | 28 +++-- .../engine/menu/domain/MenuItemView.java | 19 ++- .../engine/menu/domain/ToDataSetOutcome.java | 20 ++-- .../menu/domain/configurations/ViewBody.java | 21 +++- .../engine/menu/services/MenuItemService.java | 111 +++++++++++++++++- .../services/interfaces/IMenuItemService.java | 16 ++- .../engine/menu/utils/MenuItemUtils.java | 42 +++++-- 9 files changed, 232 insertions(+), 48 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 8f1eb13a924..eb2073029e0 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2148,14 +2148,18 @@ class ActionDelegate { } /** - * Duplicates menu item. It creates new menu_item instance with the same {@link Case#dataSet} as the provided - * item instance. The only difference is in title, menu_item_identifier and associations + * Duplicates menu item. It creates new menu_item instance with the same dataSet as the provided + * item instance. The only difference is in title, menu_item_identifier and associations. Configuration cases are + * duplicated as well. * * @param originItem Menu item instance, which is duplicated * @param newTitle Title of menu item, that is displayed in menu and tab. Cannot be empty or null. * @param newIdentifier unique menu item identifier * * @return duplicated {@link Case} instance of menu_item + * + * @throws IllegalArgumentException if the input data are invalid or the menu item of the new identifier already + * exists * */ Case duplicateMenuItem(Case originItem, I18nString newTitle, String newIdentifier) { return menuItemService.duplicateItem(originItem, newTitle, newIdentifier) @@ -2184,7 +2188,9 @@ class ActionDelegate { } /** - * todo javadoc + * @param node uri node + * + * @return folder menu item case by provided UriNode * */ Case findFolderCase(UriNode node) { return menuItemService.findFolderCase(node) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java index 0232fbacb49..de048064e33 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java @@ -4,6 +4,7 @@ import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.startup.DefaultFiltersRunner; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import lombok.Data; import lombok.NoArgsConstructor; @@ -28,7 +29,11 @@ public FilterBody(Case filterCase) { } /** - * todo javadoc + * Gets default metadata with provided filter type + * + * @param type type of the filter + * + * @return metadata containing filter type as map * */ public static Map getDefaultMetadata(String type) { Map resultMap = new HashMap<>(); @@ -43,7 +48,9 @@ public static Map getDefaultMetadata(String type) { } /** - * todo javadoc + * Transforms attributes into dataSet for {@link IDataService#setData} + * + * @return {@link ToDataSetOutcome} object with dataSet * */ public ToDataSetOutcome toDataSet() { ToDataSetOutcome outcome = new ToDataSetOutcome(); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 852c7863587..f3204e6d9c5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -5,8 +5,8 @@ import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; -import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import lombok.Data; import lombok.NoArgsConstructor; @@ -14,9 +14,7 @@ import java.util.Map; /** - * todo javadoc - * Class, that holds configurable attributes of menu item. In case of attribute addition, please update also - * {@link MenuItemBody#toDataSet(String, String, boolean)} method. + * Class, that holds configurable attributes of menu item. */ @Data @NoArgsConstructor @@ -110,36 +108,40 @@ public void setTabName(String name) { } /** - * todo javadoc + * @return true if the menu item contains view * */ public boolean hasView() { return this.view != null; } /** - * todo javadoc - * Transforms attributes into dataSet for {@link ActionDelegate#setData} + * Transforms attributes into dataSet for {@link IDataService#setData} * - * @return created dataSet from attributes + * @return {@link ToDataSetOutcome} object with dataSet */ public ToDataSetOutcome toDataSet() { return toDataSet(null, null, null); } /** - * todo javadoc + * Transforms attributes into dataSet for {@link IDataService#setData} + * + * @param viewCase case instance of view. If provided, caseRef and taskRef are initialized + * + * @return {@link ToDataSetOutcome} object with dataSet */ public ToDataSetOutcome toDataSet(Case viewCase) { return toDataSet(null, null, viewCase); } /** - * todo javadoc - * Transforms attributes into dataSet for {@link ActionDelegate#setData} + * Transforms attributes into dataSet for {@link IDataService#setData} * - * @param parentId id of parent menu item instance + * @param parentId identifier of parent menu item instance * @param nodePath uri, that represents the menu item (f.e.: "/myItem1/myItem2") - * @return created dataSet from attributes + * @param viewCase case instance of view. If provided, caseRef and taskRef are initialized + * + * @return {@link ToDataSetOutcome} object with dataSet */ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCase) { ToDataSetOutcome outcome = new ToDataSetOutcome(); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 85816e809a3..145563a2c8d 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -9,7 +9,7 @@ /** - * todo javadoc + * Here is listed and configured every configuration process available for menu items. * */ @Getter public enum MenuItemView { @@ -21,7 +21,7 @@ public enum MenuItemView { private final I18nString name; private final String identifier; /** - * todo javadoc + * List of view identifiers of views, that can be associated with the view * */ private final List allowedAssociatedViews; private final boolean isTabbed; @@ -34,7 +34,7 @@ public enum MenuItemView { } /** - * todo javadoc + * Builds enum value by the view identifier * */ public static MenuItemView fromIdentifier(String identifier) { for (MenuItemView view : MenuItemView.values()) { @@ -46,7 +46,11 @@ public static MenuItemView fromIdentifier(String identifier) { } /** - * todo javadoc + * Finds all enum values, that are tabbed or non-tabbed + * + * @param isTabbed if true, only tabbed values will be returned + * + * @return List of views based on {@link #isTabbed} * */ public static List findAllByIsTabbed(boolean isTabbed) { return Arrays.stream(MenuItemView.values()) @@ -55,7 +59,12 @@ public static List findAllByIsTabbed(boolean isTabbed) { } /** - * todo javadoc + * Finds all enum values, that are tabbed or non-tabbed and are defined in parent view as {@link #allowedAssociatedViews} + * + * @param isTabbed if true, set of views is reduced to only tabbed views + * @param parentIdentifier identifier of the view, that contains returned views in {@link #allowedAssociatedViews} + * + * @return List of views based on {@link #isTabbed} and {@link #allowedAssociatedViews} * */ public static List findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { MenuItemView parentView = fromIdentifier(parentIdentifier); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java b/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java index 183e1287ec4..7fdba433994 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/ToDataSetOutcome.java @@ -12,21 +12,17 @@ @AllArgsConstructor public class ToDataSetOutcome { private Map> dataSet; - /** - * todo javadoc - * */ - private ToDataSetOutcome associatedOutcome; public ToDataSetOutcome() { this.dataSet = new HashMap<>(); } - public ToDataSetOutcome(Map> dataSet) { - this.dataSet = dataSet; - } - /** - * todo javadoc + * Puts provided value into {@link #dataSet} according to dataSet rules. + * + * @param fieldId importId of the field + * @param fieldType type of the field + * @param fieldValue new value of the field * */ public void putDataSetEntry(String fieldId, FieldType fieldType, @Nullable Object fieldValue) { Map fieldMap = new LinkedHashMap<>(); @@ -36,7 +32,11 @@ public void putDataSetEntry(String fieldId, FieldType fieldType, @Nullable Objec } /** - * todo javadoc + * Puts provided options into {@link #dataSet} according to dataSet rules. + * + * @param fieldId importId of the field + * @param fieldType type of the field + * @param options new options of the field * */ public void putDataSetEntryOptions(String fieldId, FieldType fieldType, @Nullable Map options) { Map fieldMap = new LinkedHashMap<>(); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java index 461f441f437..39f07635e1b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -6,6 +6,7 @@ import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -24,33 +25,43 @@ public abstract class ViewBody { public abstract ViewBody getAssociatedViewBody(); public abstract MenuItemView getViewType(); /** - * todo javadoc + * Internal method, that must transform data in concrete class and add them into received outcome. Method must return + * the updated outcome. * */ protected abstract ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome); /** - * todo javadoc + * Checks if the view has associated view + * + * @return true if this view has associated view * */ public boolean hasAssociatedView() { return this.getAssociatedViewBody() != null; } /** - * todo javadoc + * @return returns process identifier for this view * */ public String getViewProcessIdentifier() { return getViewType().getIdentifier() + "_configuration"; } /** - * todo javadoc + * Transforms data of this class into {@link ToDataSetOutcome}, which contains prepared data for the {@link IDataService#setData} + * + * @return {@link ToDataSetOutcome} object containing dataSet * */ public ToDataSetOutcome toDataSet() { return toDataSet(null, null); }; /** - * todo javadoc + * Transforms data of this class into {@link ToDataSetOutcome}, which contains prepared data for the {@link IDataService#setData} + * + * @param associatedViewCase case instance of associated view. If provided, caseRef and taskRef are initialized. + * @param filterCase case instance of filter. If provided, caseRef is initialized + * + * @return {@link ToDataSetOutcome} object containing dataSet * */ public ToDataSetOutcome toDataSet(Case associatedViewCase, Case filterCase) { ToDataSetOutcome outcome = new ToDataSetOutcome(); diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index 3f72a466199..34acd25d8ad 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -37,7 +37,6 @@ @Service @RequiredArgsConstructor public class MenuItemService implements IMenuItemService { - // todo javadoc protected final IWorkflowService workflowService; protected final ITaskService taskService; protected final IDataService dataService; @@ -46,6 +45,13 @@ public class MenuItemService implements IMenuItemService { protected static final String DEFAULT_FOLDER_ICON = "folder"; + /** + * Creates new filter case + * + * @param body filter data used for creation + * + * @return initialized filter case instance with the provided data + * */ @Override public Case createFilter(FilterBody body) throws TransitionNotExecutableException { IUser loggedUser = userService.getLoggedOrSystem(); @@ -58,6 +64,14 @@ public Case createFilter(FilterBody body) throws TransitionNotExecutableExceptio return filterCase; } + /** + * Updates existing filter case + * + * @param filterCase filter to be updated + * @param body data values used for update + * + * @return updated filter case instance + * */ @Override public Case updateFilter(Case filterCase, FilterBody body) { filterCase.setIcon(body.getIcon()); @@ -68,6 +82,16 @@ public Case updateFilter(Case filterCase, FilterBody body) { return filterCase; } + + /** + * Creates menu item case and it's configuration cases + * + * @param body data used for creation + * + * @return initialized menu item instance with the provided data + * + * @throws IllegalArgumentException if the provided menu identifier already exists + * */ @Override public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableException { log.debug("Creation of menu item case with identifier [{}] started.", body.getIdentifier()); @@ -103,6 +127,14 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce return menuItemCase; } + /** + * Updates menu item case and it's configuration cases + * + * @param itemCase menu item case to be updated + * @param body data used for update + * + * @return updated menu item case (configuration cases are updated, but not returned) + * */ @Override public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNotExecutableException { log.debug("Update of menu item case with identifier [{}] started.", body.getIdentifier()); @@ -120,6 +152,14 @@ public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNo return itemCase; } + /** + * Creates or updates menu item. At first menu item is searched by identifier. If found, then menu item will be + * updated. If not, menu item will be created + * + * @param body data used for the update or creation + * + * @return updated or created menu item case + * */ @Override public Case createOrUpdateMenuItem(MenuItemBody body) throws TransitionNotExecutableException { Case itemCase = findMenuItem(MenuItemUtils.sanitize(body.getIdentifier())); @@ -130,6 +170,14 @@ public Case createOrUpdateMenuItem(MenuItemBody body) throws TransitionNotExecut } } + /** + * Creates or ignore menu item. At first menu item is searched by identifier. If found, then nothing will happen. + * If not, menu item will be created + * + * @param body data used for the creation + * + * @return ignored or created menu item case + * */ @Override public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecutableException { Case itemCase = findMenuItem(body.getIdentifier()); @@ -142,6 +190,13 @@ public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecut } } + /** + * Finds menu item by identifier. + * + * @param identifier identifier of the menu item + * + * @return Found menu item case. If not found, null will be returned + * */ @Override public Case findMenuItem(String identifier) { // return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.menu_item_identifier.textValue.keyword:\"$menuItemIdentifier\"" as String) @@ -150,6 +205,14 @@ public Case findMenuItem(String identifier) { return workflowService.searchOne(predicate); } + /** + * Finds menu item by uri and name. + * + * @param uri string id of UriNode where the item exists + * @param name name of the menu item + * + * @return Found menu item case. If not found, null will be returned + * */ @Override public Case findMenuItem(String uri, String name) { UriNode uriNode = uriService.findByUri(uri); @@ -160,6 +223,13 @@ public Case findMenuItem(String uri, String name) { return workflowService.searchOne(predicate); } + /** + * Finds folder case by UriNode + * + * @param node UriNode, which folder case represents + * + * @return Found folder menu item case. If not found, null will be returned + * */ @Override public Case findFolderCase(UriNode node) { // todo elastic problem @@ -169,12 +239,29 @@ public Case findFolderCase(UriNode node) { return workflowService.searchOne(predicate); } + /** + * Checks if the menu item exists + * + * @param identifier identifier of the menu item + * + * @return true if the menu item exists + * */ @Override public boolean existsMenuItem(String identifier) { // return countCasesElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.menu_item_identifier.fulltextValue.keyword:\"$menuItemIdentifier\"") > 0 return findMenuItem(identifier) != null; } + /** + * Changes location of menu item. If non-existing location is provided, the new location is created and then the + * item is moved. Cyclic destination path is forbidden (f.e. from "/my_node" to + * "/my_node/my_node2" + * + * @param itemCase Instance of menu_item to be moved + * @param destUri destination path where the item will be moved. F.e. "/my_new_node" + * + * @throws IllegalArgumentException if the path is forbidden + * */ @Override public void moveItem(Case itemCase, String destUri) throws TransitionNotExecutableException { log.debug("Move of menu item case [{}] started. Destination path [{}]", itemCase.getStringId(), destUri); @@ -216,6 +303,20 @@ public void moveItem(Case itemCase, String destUri) throws TransitionNotExecutab log.debug("Moved menu item case [{}]. Destination path was [{}]", itemCase.getStringId(), destUri); } + /** + * Duplicates menu item. It creates new menu_item instance with the same dataSet as the provided + * item instance. The only difference is in title, menu_item_identifier and associations. Configuration cases are + * duplicated as well. + * + * @param originItem Menu item instance, which is duplicated + * @param newTitle Title of menu item, that is displayed in menu and tab. Cannot be empty or null. + * @param newIdentifier unique menu item identifier + * + * @return duplicated {@link Case} instance of menu_item + * + * @throws IllegalArgumentException if the input data are invalid or the menu item of the new identifier already + * exists + * */ @Override public Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException { log.debug("Duplication of menu item case [{}] started.", originItem.getStringId()); @@ -278,6 +379,14 @@ public Case duplicateItem(Case originItem, I18nString newTitle, String newIdenti return workflowService.findOne(duplicated.getStringId()); } + /** + * Removes child menu item from the dataSet of the folder menu item case + * + * @param folderId menu item identifier of the folder case + * @param childItem menu item case of the child item to be removed + * + * @return updated folder menu item case + * */ @Override public Case removeChildItemFromParent(String folderId, Case childItem) { Case parentFolder = workflowService.findOne(folderId); diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java index 8ffba28cf29..7bdd4c26b78 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java @@ -7,6 +7,7 @@ import com.netgrif.application.engine.petrinet.domain.UriNode; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.petrinet.domain.dataset.MapOptionsField; import java.util.Map; import java.util.stream.Collectors; @@ -28,7 +29,12 @@ public interface IMenuItemService { Case removeChildItemFromParent(String folderId, Case childItem); /** - * todo javadoc + * Gets all tabbed or non-tabbed views + * + * @param isTabbed if true, only tabbed views will be returned + * + * @return All available views defined in {@link MenuItemView} in consideration of input value. Views are returned as + * options for {@link MapOptionsField} * */ default Map getAvailableViewsAsOptions(boolean isTabbed) { return MenuItemView.findAllByIsTabbed(isTabbed).stream() @@ -36,7 +42,13 @@ default Map getAvailableViewsAsOptions(boolean isTabbed) { } /** - * todo javadoc + * Gets all tabbed or non-tabbed views + * + * @param isTabbed if true, only tabbed views will be returned + * @param viewIdentifier identifier of view (defined in {@link MenuItemView}), which is parent to returned views + * + * @return All available views defined in {@link MenuItemView} in consideration of input values. Views are returned as + * options for {@link MapOptionsField} * */ default Map getAvailableViewsAsOptions(boolean isTabbed, String viewIdentifier) { int index = viewIdentifier.lastIndexOf("_configuration"); diff --git a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java index e486fa18c25..d1fcaf8e590 100644 --- a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java +++ b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java @@ -3,6 +3,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemConstants; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.workflow.domain.TaskPair; +import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService; import java.text.Normalizer; import java.util.List; @@ -10,7 +11,12 @@ public class MenuItemUtils { /** - * todo javadoc + * Sanitizes input. Removes any diacritical marks, replaces any special character with delimiter and lowers the + * characters + * + * @param input input string to be sanitized + * + * @return sanitized input string * */ public static String sanitize(String input) { if (input == null) { @@ -24,7 +30,12 @@ public static String sanitize(String input) { } /** - * todo javadoc + * Finds task id in the provided case instance by transition id + * + * @param useCase case instance containing the task id to be found + * @param transId transition identifier of the task + * + * @return id of found task or null otherwise */ public static String findTaskIdInCase(Case useCase, String transId) { if (useCase == null || transId == null) { @@ -43,7 +54,13 @@ public static String findTaskIdInCase(Case useCase, String transId) { } /** - * todo javadoc + * This method is mainly used for {@link IMenuItemService#moveItem(Case, String)} + * + * @param folderItem case instance of folder menu item + * @param destUri path of the uri node + * + * @return true, if the nodePath would become cyclic to folderItem's current nodePath after item move. F.e. + * "/node1/node2" would be cyclic to "/node1/node2/node3" after move * */ public static boolean isCyclicNodePath(Case folderItem, String destUri) { String oldNodePath = (String) folderItem.getFieldValue(MenuItemConstants.FIELD_NODE_PATH); @@ -51,7 +68,11 @@ public static boolean isCyclicNodePath(Case folderItem, String destUri) { } /** - * todo javadoc + * @param useCase case instance where the caseRef exists + * @param caseRefId id of caseRef field + * + * @return List of case ids inside caseRef field. Returns null if the field doesn't exist or the field's value is + * null. * */ @SuppressWarnings("unchecked") public static List getCaseIdsFromCaseRef(Case useCase, String caseRefId) { @@ -63,7 +84,10 @@ public static List getCaseIdsFromCaseRef(Case useCase, String caseRefId) } /** - * todo javadoc + * @param useCase case instance where the caseRef exists + * @param caseRefId id of caseRef field + * + * @return Case id inside caseRef field. Returns null if the field doesn't exist or the caseRef is empty. * */ public static String getCaseIdFromCaseRef(Case useCase, String caseRefId) { List caseIds = getCaseIdsFromCaseRef(useCase, caseRefId); @@ -74,14 +98,18 @@ public static String getCaseIdFromCaseRef(Case useCase, String caseRefId) { } /** - * todo javadoc + * @param menuItemCase case instance of menu item + * + * @return true if the menu item contains view * */ public static boolean hasView(Case menuItemCase) { return getCaseIdFromCaseRef(menuItemCase, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) != null; } /** - * todo javadoc + * @param folderCase case instance of folder menu item + * + * @return true if the folder case contains any child menu item cases * */ public static boolean hasFolderChildren(Case folderCase) { List childIds = MenuItemUtils.getCaseIdsFromCaseRef(folderCase, MenuItemConstants.FIELD_CHILD_ITEM_IDS); From 255340f90b936629d0cc32eb8e2020e2afbe9e65 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 11 Feb 2025 08:40:16 +0100 Subject: [PATCH 009/174] [NAE-2051] Implement configurable view in menu items - add translations --- .../engine/menu/domain/MenuItemView.java | 9 +++++---- .../engine-processes/menu/menu_item.xml | 9 ++++++--- .../menu/tabbed_case_view_configuration.xml | 16 ++++++++++------ .../menu/tabbed_task_view_configuration.xml | 2 ++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 145563a2c8d..3c7bea1c2ee 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @@ -13,10 +14,10 @@ * */ @Getter public enum MenuItemView { - // todo translations - TABBED_CASE_VIEW(new I18nString("Tabbed case view"), "tabbed_case_view", - List.of("tabbed_task_view"), true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view"), "tabbed_task_view", List.of(), true); + TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", + "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), true), + TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", + "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true); private final I18nString name; private final String identifier; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 0c0cfd74d7a..9bb2f3a7f71 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -429,7 +429,6 @@ - use_tabbed_view Do you want to use view with tabs? @@ -472,7 +471,6 @@ view_configuration_type - Pick view type menuItemService.getAvailableViewsAsOptions(false) @@ -626,6 +624,9 @@ Pridať Automatické zvolenie zobrazenia Po automatickom zvolení sa dané zobrazenie používateľovi otvorí + Použiť zobrazenie v taboch? + Vybrať zobrazenie + Nastavenie zobrazenia Ikonevorschau @@ -671,6 +672,9 @@ Teile der Ziel URI Automatische Anzeigeauswahl Wenn ausgewählt, wird die Ansicht automatisch geöffnet + Möchten Sie die Ansicht mit Registerkarten verwenden? + Wählen Sie einen Ansichtstyp + Die Ansichtskonfiguration @@ -940,7 +944,6 @@ configuration_view 4 grid - View configuration use_custom_view diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 1fdc487b7e0..b9f780ad642 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -369,7 +369,6 @@ view_configuration_type - Pick view type menuItemService.getAvailableViewsAsOptions(false, useCase.processIdentifier) @@ -395,7 +394,6 @@ -// todo prejst preklady Názov tlačidla "Nová inštancia" Identifikátor ikony tlačidla "Nová inštancia" @@ -426,6 +424,10 @@ Súčasný filter Zobrazenie prípadov Všeobecné + Použiť predvolené hlavičky + Vybrať zobrazenie + Nastavenie + Asociované zobrazenie Schaltflächentitel "Neuer Fall" @@ -457,6 +459,10 @@ "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen Menüeintrageinstellungen Fallansicht + Benutzerdefinierte Standardheader verwenden? + Wählen Sie einen Ansichtstyp + Einstellungen + zugehörige Ansicht @@ -476,8 +482,7 @@ settings 496 112 - - + settings admin @@ -836,8 +841,7 @@ associated_view 4 grid - - Next view + Associated view view_configuration_type diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index d969f5db746..66caae1268f 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -341,6 +341,7 @@ Súčasný filter Zobrazenie úloh Všeobecné + Vymazať filter Neue Filter auswählen @@ -366,6 +367,7 @@ "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen Menüeintrageinstellungen Aufgabenansicht + Filter entfernen From 2bde47c756c1be6158ef2d2d4d8669071f69a748 Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 11 Feb 2025 17:27:06 +0100 Subject: [PATCH 010/174] [NAE-2051] Implement configurable view in menu items - remove redundant MenuItemBody attribute - update menu_item and tabbed_case_view_configuration dataGroups --- .../dataset/logic/action/ActionDelegate.groovy | 18 ------------------ .../engine/menu/domain/MenuItemBody.java | 3 +-- .../engine-processes/menu/menu_item.xml | 15 +++++---------- .../menu/tabbed_case_view_configuration.xml | 9 ++------- 4 files changed, 8 insertions(+), 37 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index eb2073029e0..68219a040fa 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -1728,7 +1728,6 @@ class ActionDelegate { body.setUseTabbedView(true) body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) - initializeViewType(body) return menuItemService.createMenuItem(body) } @@ -1758,7 +1757,6 @@ class ActionDelegate { body.setUseTabbedView(true) body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) - initializeViewType(body) return menuItemService.createMenuItem(body) } @@ -1789,7 +1787,6 @@ class ActionDelegate { body.setUseTabbedView(true) body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) - initializeViewType(body) return menuItemService.createMenuItem(body) } @@ -1819,7 +1816,6 @@ class ActionDelegate { body.setUseTabbedView(true) body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) - initializeViewType(body) return menuItemService.createMenuItem(body) } @@ -1858,7 +1854,6 @@ class ActionDelegate { body.setUseTabbedView(true) body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) - initializeViewType(body) return menuItemService.createMenuItem(body) } @@ -2098,14 +2093,6 @@ class ActionDelegate { return menuItemService.createMenuItem(body) } - protected void initializeViewType(MenuItemBody body) { - if (body.view.filterBody.type == "Case") { - body.setViewType(MenuItemView.TABBED_CASE_VIEW) - } else { - body.setViewType(MenuItemView.TABBED_TASK_VIEW) - } - } - protected ViewBody createLegacyMenuItemViews(Case filterCase, List caseDefaultHeaders = null, List taskDefaultHeaders = null) { FilterBody body = new FilterBody(filterCase) @@ -2375,7 +2362,6 @@ class ActionDelegate { filterBody.setVisibility(DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE) body.setView(createLegacyMenuItemViews(filterBody, defaultHeaders)) - initializeViewType(body) return menuItemService.createOrUpdateMenuItem(body) } @@ -2405,7 +2391,6 @@ class ActionDelegate { body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseTabbedView(true) body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) - initializeViewType(body) return createOrUpdateMenuItem(body) } @@ -2455,7 +2440,6 @@ class ActionDelegate { filterBody.setVisibility(filterVisibility) filterBody.setMetadata(filterMetadata as Map) body.setView(createLegacyMenuItemViews(filterBody, itemCaseDefaultHeaders, itemTaskDefaultHeaders)) - initializeViewType(body) return menuItemService.createOrUpdateMenuItem(body) } @@ -2501,7 +2485,6 @@ class ActionDelegate { filterBody.setVisibility(filterVisibility) filterBody.setMetadata(filterMetadata as Map) body.setView(createLegacyMenuItemViews(filterBody)) - initializeViewType(body) return menuItemService.createOrUpdateMenuItem(body) } @@ -2540,7 +2523,6 @@ class ActionDelegate { filterBody.setMetadata(filterMetadata as Map) body.setView(createLegacyMenuItemViews(filterBody)) - initializeViewType(body) return menuItemService.createOrIgnoreMenuItem(body) } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index f3204e6d9c5..1f45a2c8076 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -34,7 +34,6 @@ public class MenuItemBody { private String tabIcon; private boolean useTabIcon = true; private I18nString tabName; - private MenuItemView viewType; private ViewBody view; public MenuItemBody(I18nString name, String icon) { @@ -171,7 +170,7 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas if (viewCase != null) { outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, - this.viewType.getIdentifier()); + this.view.getViewType().getIdentifier()); outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID, FieldType.CASE_REF, List.of(viewCase.getStringId())); String taskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_SETTINGS_ID); diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 9bb2f3a7f71..5dc42e4a6ad 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -724,9 +724,10 @@ - pre_general + general_0 4 grid + General menu_item_identifier @@ -755,12 +756,6 @@ outline - - - general_0 - 4 - grid - General menu_name @@ -768,7 +763,7 @@ 0 - 0 + 1 1 2 @@ -782,7 +777,7 @@ 2 - 0 + 1 1 1 @@ -796,7 +791,7 @@ 3 - 0 + 1 1 1 diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index b9f780ad642..b33e9b7bbde 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -559,11 +559,6 @@ standard - - - current_filter - 4 - grid filter_header @@ -571,7 +566,7 @@ 0 - 0 + 2 1 4 @@ -585,7 +580,7 @@ 0 - 1 + 3 1 4 From 570a7adf3312b77b6243281371c9201fbd7fa84c Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 12 Feb 2025 17:33:29 +0100 Subject: [PATCH 011/174] [NAE-2051] Implement configurable view in menu items - update MenuItemService find methods - fix MenuItemApiTest.testDuplicateMenuItem --- .../engine/menu/services/MenuItemService.java | 54 ++++++++++++------- .../engine/action/MenuItemApiTest.groovy | 2 + .../resources/petriNets/filter_api_test.xml | 1 - 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index 34acd25d8ad..eb6a051546d 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -3,6 +3,8 @@ import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; import com.netgrif.application.engine.auth.service.interfaces.IUserService; +import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; +import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.MenuItemConstants; @@ -21,14 +23,13 @@ import com.netgrif.application.engine.startup.FilterRunner; import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.workflow.domain.Case; -import com.netgrif.application.engine.workflow.domain.QCase; import com.netgrif.application.engine.workflow.domain.Task; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; -import com.querydsl.core.types.Predicate; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.*; import org.springframework.stereotype.Service; import java.util.*; @@ -42,6 +43,7 @@ public class MenuItemService implements IMenuItemService { protected final IDataService dataService; protected final IUserService userService; protected final IUriService uriService; + protected final IElasticCaseService elasticCaseService; protected static final String DEFAULT_FOLDER_ICON = "folder"; @@ -199,10 +201,9 @@ public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecut * */ @Override public Case findMenuItem(String identifier) { - // return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.menu_item_identifier.textValue.keyword:\"$menuItemIdentifier\"" as String) - Predicate predicate = QCase.case$.processIdentifier.eq(FilterRunner.MENU_NET_IDENTIFIER) - .and(QCase.case$.dataSet.get("menu_item_identifier").value.eq(identifier)); - return workflowService.searchOne(predicate); + String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", + FilterRunner.MENU_NET_IDENTIFIER, MenuItemConstants.FIELD_IDENTIFIER, identifier); + return findCase(FilterRunner.MENU_NET_IDENTIFIER, query); } /** @@ -216,11 +217,9 @@ public Case findMenuItem(String identifier) { @Override public Case findMenuItem(String uri, String name) { UriNode uriNode = uriService.findByUri(uri); -// return findCaseElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND title.keyword:\"$name\" AND uriNodeId:\"$uriNode.stringId\"") - Predicate predicate = QCase.case$.processIdentifier.eq(FilterRunner.MENU_NET_IDENTIFIER) - .and(QCase.case$.title.eq(name)) - .and(QCase.case$.uriNodeId.eq(uriNode.getStringId())); - return workflowService.searchOne(predicate); + String query = String.format("processIdentifier:%s AND title.keyword:\"%s\" AND uriNodeId:\"%s\"", + FilterRunner.MENU_NET_IDENTIFIER, name, uriNode.getStringId()); + return findCase(FilterRunner.MENU_NET_IDENTIFIER, query); } /** @@ -232,11 +231,9 @@ public Case findMenuItem(String uri, String name) { * */ @Override public Case findFolderCase(UriNode node) { - // todo elastic problem -// return findCaseElastic("processIdentifier:$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER AND dataSet.nodePath.textValue.keyword:\"$node.uriPath\"") - Predicate predicate = QCase.case$.processIdentifier.eq(FilterRunner.MENU_NET_IDENTIFIER) - .and(QCase.case$.dataSet.get("nodePath").value.eq(node.getUriPath())); - return workflowService.searchOne(predicate); + String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", + FilterRunner.MENU_NET_IDENTIFIER, MenuItemConstants.FIELD_NODE_PATH, node.getUriPath()); + return findCase(FilterRunner.MENU_NET_IDENTIFIER, query); } /** @@ -248,8 +245,9 @@ public Case findFolderCase(UriNode node) { * */ @Override public boolean existsMenuItem(String identifier) { - // return countCasesElastic("processIdentifier:\"$FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER\" AND dataSet.menu_item_identifier.fulltextValue.keyword:\"$menuItemIdentifier\"") > 0 - return findMenuItem(identifier) != null; + String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", + FilterRunner.MENU_NET_IDENTIFIER, MenuItemConstants.FIELD_IDENTIFIER, identifier); + return countCases(FilterRunner.MENU_NET_IDENTIFIER, query) > 0; } /** @@ -400,6 +398,26 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { return workflowService.save(parentFolder); } + protected Case findCase(String processIdentifier, String query) { + CaseSearchRequest request = CaseSearchRequest.builder() + .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) + .query(query) + .build(); + Page resultPage = elasticCaseService.search(List.of(request), userService.getLoggedOrSystem().transformToLoggedUser(), + PageRequest.of(0, 1), Locale.getDefault(), false); + + return resultPage.hasContent() ? resultPage.getContent().get(0) : null; + } + + protected long countCases(String processIdentifier, String query) { + CaseSearchRequest request = CaseSearchRequest.builder() + .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) + .query(query) + .build(); + return elasticCaseService.count(List.of(request), userService.getLoggedOrSystem().transformToLoggedUser(), + Locale.getDefault(), false); + } + protected Case duplicateView(Case viewCase) throws TransitionNotExecutableException { Case duplicatedAssociatedViewCase = null; if (MenuItemUtils.hasView(viewCase)) { diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index fa983411ebe..bbd721fb593 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -268,6 +268,8 @@ class MenuItemApiTest { void testDuplicateMenuItem() { String starterUri = "/netgrif/test" Case apiCase = createMenuItem(starterUri, "new_menu_item") + Thread.sleep(2000) + String itemId = apiCase.dataSet["menu_stringId"].value Case origin = workflowService.findOne(itemId) Case testFolder = workflowService.findOne((origin.dataSet[MenuItemConstants.FIELD_PARENT_ID].value as ArrayList)[0]) diff --git a/src/test/resources/petriNets/filter_api_test.xml b/src/test/resources/petriNets/filter_api_test.xml index 7f537c2253f..ab00d976034 100644 --- a/src/test/resources/petriNets/filter_api_test.xml +++ b/src/test/resources/petriNets/filter_api_test.xml @@ -117,7 +117,6 @@ "defaultSearchCategories": false, "inheritAllowedNets" : false ] - awd() createOrUpdateMenuItemAndFilter(uri.value, identifier.value, title.value, query.value, "Case", "private", allowed_nets.value.split(",") as List, icon.value, ["role_2": "filter_api_test"], [:], ["meta-title","meta-title","meta-title"], ["meta-title","meta-title","meta-title"], metadata) From 6c71bdba3bec9464056137a67197c6acc4ed9233 Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 12 Feb 2025 17:53:26 +0100 Subject: [PATCH 012/174] [NAE-2051] Implement configurable view in menu items - update MenuItemBody.setView --- .../netgrif/application/engine/menu/domain/MenuItemBody.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 1f45a2c8076..8440705594a 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -106,6 +106,11 @@ public void setTabName(String name) { this.tabName = new I18nString(name); } + public void setView(ViewBody viewBody) { + this.view = viewBody; + this.useTabbedView = viewBody.getViewType().isTabbed(); + } + /** * @return true if the menu item contains view * */ From 4cef68862e4e775ae70a73c778ffd5fa64263325 Mon Sep 17 00:00:00 2001 From: Machac Date: Thu, 13 Feb 2025 13:00:39 +0100 Subject: [PATCH 013/174] [NAE-2053] Optimize ElasticCaseService queries to eliminate maxClauseCount error - ElasticCaseService, ElasticViewPermissionService: refactored queries to use termsQuery and filter, reducing clause count. - pom.xml: updated spring-session to spring-session-core. --- docker-compose.yml | 2 +- pom.xml | 5 +- .../elastic/service/ElasticCaseService.java | 70 ++++++++----------- .../service/ElasticViewPermissionService.java | 64 +++++++++-------- 4 files changed, 69 insertions(+), 72 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8c0780e3771..6ca387520cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: memory: "512M" docker-elastic: - image: elasticsearch:7.17.4 + image: elasticsearch:7.17.26 environment: - cluster.name=elasticsearch - discovery.type=single-node diff --git a/pom.xml b/pom.xml index 98e0fe201c4..62ba500b42d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.netgrif application-engine - 6.2.9-SNAPSHOT + 6.2.10 jar NETGRIF Application Engine @@ -441,8 +441,7 @@ org.springframework.session - spring-session - 2.0.0.M2 + spring-session-core xmlunit diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java index d8881d0385d..e8dcfe1ebd5 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java @@ -142,8 +142,8 @@ public Page search(List requests, LoggedUser user, Page List casePage; long total; if (query != null) { - SearchHits hits = template.search(query, ElasticCase.class, IndexCoordinates.of(caseIndex)); - Page indexedCases = (Page)SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(hits, query.getPageable())); + SearchHits hits = template.search(query, ElasticCase.class, IndexCoordinates.of(caseIndex)); + Page indexedCases = (Page) SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(hits, query.getPageable())); casePage = workflowService.findAllById(indexedCases.get().map(ElasticCase::getStringId).collect(Collectors.toList())); total = indexedCases.getTotalElements(); } else { @@ -169,14 +169,16 @@ public long count(List requests, LoggedUser user, Locale loca } private NativeSearchQuery buildQuery(List requests, LoggedUser user, Pageable pageable, Locale locale, Boolean isIntersection) { - List singleQueries = requests.stream().map(request -> buildSingleQuery(request, user, locale)).collect(Collectors.toList()); + List singleQueries = requests.stream() + .map(request -> buildSingleQuery(request, user, locale)) + .collect(Collectors.toList()); if (isIntersection && !singleQueries.stream().allMatch(Objects::nonNull)) { // one of the queries evaluates to empty set => the entire result is an empty set return null; } else if (!isIntersection) { singleQueries = singleQueries.stream().filter(Objects::nonNull).collect(Collectors.toList()); - if (singleQueries.size() == 0) { + if (singleQueries.isEmpty()) { // all queries result in an empty set => the entire result is an empty set return null; } @@ -209,10 +211,7 @@ private BoolQueryBuilder buildSingleQuery(CaseSearchRequest request, LoggedUser // TODO: filtered query https://stackoverflow.com/questions/28116404/filtered-query-using-nativesearchquerybuilder-in-spring-data-elasticsearch - if (resultAlwaysEmpty) - return null; - else - return query; + return resultAlwaysEmpty ? null : query; } private void buildPetriNetQuery(CaseSearchRequest request, LoggedUser user, BoolQueryBuilder query) { @@ -220,17 +219,22 @@ private void buildPetriNetQuery(CaseSearchRequest request, LoggedUser user, Bool return; } - BoolQueryBuilder petriNetQuery = boolQuery(); + List identifiers = request.process.stream() + .filter(p -> p.identifier != null) + .map(p -> p.identifier) + .collect(Collectors.toList()); + List processIds = request.process.stream() + .filter(p -> p.processId != null) + .map(p -> p.processId) + .collect(Collectors.toList()); - for (CaseSearchRequest.PetriNet process : request.process) { - if (process.identifier != null) { - petriNetQuery.should(termQuery("processIdentifier", process.identifier)); - } - if (process.processId != null) { - petriNetQuery.should(termQuery("processId", process.processId)); - } + BoolQueryBuilder petriNetQuery = boolQuery(); + if (!identifiers.isEmpty()) { + petriNetQuery.should(termsQuery("processIdentifier", identifiers)); + } + if (!processIds.isEmpty()) { + petriNetQuery.should(termsQuery("processId", processIds)); } - query.filter(petriNetQuery); } @@ -332,13 +336,7 @@ private void buildRoleQuery(CaseSearchRequest request, BoolQueryBuilder query) { if (request.role == null || request.role.isEmpty()) { return; } - - BoolQueryBuilder roleQuery = boolQuery(); - for (String roleId : request.role) { - roleQuery.should(termQuery("enabledRoles", roleId)); - } - - query.filter(roleQuery); + query.filter(termsQuery("enabledRoles", request.role)); } /** @@ -416,20 +414,14 @@ private void buildCaseIdQuery(CaseSearchRequest request, BoolQueryBuilder query) if (request.stringId == null || request.stringId.isEmpty()) { return; } - - BoolQueryBuilder caseIdQuery = boolQuery(); - request.stringId.forEach(caseId -> caseIdQuery.should(termQuery("stringId", caseId))); - query.filter(caseIdQuery); + query.filter(termsQuery("stringId", request.stringId)); } private void buildUriNodeIdQuery(CaseSearchRequest request, BoolQueryBuilder query) { if (request.uriNodeId == null || request.uriNodeId.isEmpty()) { return; } - - BoolQueryBuilder caseIdQuery = boolQuery(); - caseIdQuery.should(termQuery("uriNodeId", request.uriNodeId)); - query.filter(caseIdQuery); + query.filter(termQuery("uriNodeId", request.uriNodeId)); } /** @@ -458,14 +450,12 @@ private boolean buildGroupQuery(CaseSearchRequest request, LoggedUser user, Loca Map processQuery = new HashMap<>(); processQuery.put("group", request.group); List groupProcesses = this.petriNetService.search(processQuery, user, new FullPageRequest(), locale).getContent(); - if (groupProcesses.size() == 0) + if (groupProcesses.isEmpty()) return true; - - BoolQueryBuilder groupQuery = boolQuery(); - groupProcesses.stream().map(PetriNetReference::getIdentifier) - .map(netIdentifier -> termQuery("processIdentifier", netIdentifier)) - .forEach(groupQuery::should); - query.filter(groupQuery); + List identifiers = groupProcesses.stream() + .map(PetriNetReference::getIdentifier) + .collect(Collectors.toList()); + query.filter(termsQuery("processIdentifier", identifiers)); return false; } -} \ No newline at end of file +} diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticViewPermissionService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticViewPermissionService.java index b3c707b6403..cc736dd3480 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticViewPermissionService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticViewPermissionService.java @@ -8,11 +8,13 @@ public abstract class ElasticViewPermissionService { protected void buildViewPermissionQuery(BoolQueryBuilder query, LoggedUser user) { - BoolQueryBuilder viewPermsExists = boolQuery(); - BoolQueryBuilder viewPermNotExists = boolQuery(); - viewPermsExists.should(existsQuery("viewRoles")); - viewPermsExists.should(existsQuery("viewUserRefs")); - viewPermNotExists.mustNot(viewPermsExists); + // Check if viewRoles or viewUserRefs exist + BoolQueryBuilder viewPermsExists = boolQuery() + .should(existsQuery("viewRoles")) + .should(existsQuery("viewUserRefs")); + // Condition where these attributes do NOT exist + BoolQueryBuilder viewPermNotExists = boolQuery() + .mustNot(viewPermsExists); /* Build positive view role query */ BoolQueryBuilder positiveViewRole = buildPositiveViewRoleQuery(viewPermNotExists, user); @@ -38,42 +40,45 @@ protected void buildViewPermissionQuery(BoolQueryBuilder query, LoggedUser user) query.filter(permissionQuery); } + /** + * Build a positive view role query using termsQuery for efficiency. + * This reduces the number of clauses by sending all roles at once. + */ private BoolQueryBuilder buildPositiveViewRoleQuery(BoolQueryBuilder viewPermNotExists, LoggedUser user) { BoolQueryBuilder positiveViewRole = boolQuery(); - BoolQueryBuilder positiveViewRoleQuery = boolQuery(); - for (String roleId : user.getProcessRoles()) { - positiveViewRoleQuery.should(termQuery("viewRoles", roleId)); + if (!user.getProcessRoles().isEmpty()) { + positiveViewRole.should(termsQuery("viewRoles", user.getProcessRoles())); } positiveViewRole.should(viewPermNotExists); - positiveViewRole.should(positiveViewRoleQuery); return positiveViewRole; } + /** + * Build a negative view role query by excluding negative roles. + */ private BoolQueryBuilder buildNegativeViewRoleQuery(LoggedUser user) { BoolQueryBuilder negativeViewRole = boolQuery(); - BoolQueryBuilder negativeViewRoleQuery = boolQuery(); - for (String roleId : user.getProcessRoles()) { - negativeViewRoleQuery.should(termQuery("negativeViewRoles", roleId)); + if (!user.getProcessRoles().isEmpty()) { + negativeViewRole.mustNot(termsQuery("negativeViewRoles", user.getProcessRoles())); } - negativeViewRole.mustNot(negativeViewRoleQuery); return negativeViewRole; } + /** + * Build a positive view user query using filter (as score is not needed). + */ private BoolQueryBuilder buildPositiveViewUser(BoolQueryBuilder viewPermNotExists, LoggedUser user) { - BoolQueryBuilder positiveViewUser = boolQuery(); - BoolQueryBuilder positiveViewUserQuery = boolQuery(); - positiveViewUserQuery.must(termQuery("viewUsers", user.getId())); - positiveViewUser.should(viewPermNotExists); - positiveViewUser.should(positiveViewUserQuery); - return positiveViewUser; + return boolQuery() + .should(viewPermNotExists) + .filter(termQuery("viewUsers", user.getId())); } + /** + * Build a negative view user query to exclude the specified user. + */ private BoolQueryBuilder buildNegativeViewUser(LoggedUser user) { - BoolQueryBuilder negativeViewUser = boolQuery(); - BoolQueryBuilder negativeViewUserQuery = boolQuery(); - negativeViewUserQuery.should(termQuery("negativeViewUsers", user.getId())); - negativeViewUser.mustNot(negativeViewUserQuery); - return negativeViewUser; + return boolQuery() + .mustNot(termQuery("negativeViewUsers", user.getId())); } private BoolQueryBuilder setMinus(BoolQueryBuilder positiveSet, BoolQueryBuilder negativeSet) { @@ -83,10 +88,13 @@ private BoolQueryBuilder setMinus(BoolQueryBuilder positiveSet, BoolQueryBuilder return positiveSetMinusNegativeSet; } + /** + * Unions two queries using OR with a minimum_should_match of 1. + */ private BoolQueryBuilder union(BoolQueryBuilder setA, BoolQueryBuilder setB) { - BoolQueryBuilder unionSet = boolQuery(); - unionSet.should(setA); - unionSet.should(setB); - return unionSet; + return boolQuery() + .should(setA) + .should(setB) + .minimumShouldMatch(1); } } From 06d484d7859a1a4eeec3b2d61b785141c3dd76d9 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Thu, 13 Feb 2025 13:16:17 +0100 Subject: [PATCH 014/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add option to change profile and login urls --- .../engine-processes/dashboard_management.xml | 92 +++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml index a08c114f8bc..ddf56972c8c 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_management.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -307,6 +307,19 @@ Profile in dashboard toolbar Should dashboard toolbar contains profile button false + + profile_dashboard_toolbar_set + + + profile_dashboard_toolbar: f.profile_dashboard_toolbar, + profile_url: f.profile_url, + trans: t.configuration; + + make profile_url, visible on trans when { !profile_dashboard_toolbar.value } + make profile_url, editable on trans when { profile_dashboard_toolbar.value } + + + language_dashboard_toolbar @@ -319,11 +332,36 @@ Logout in dashboard toolbar Should dashboard toolbar contains logout button false + + logout_dashboard_toolbar_set + + + logout_dashboard_toolbar: f.logout_dashboard_toolbar, + login_url: f.login_url, + trans: t.configuration; + + make login_url, visible on trans when { !logout_dashboard_toolbar.value } + make login_url, editable on trans when { logout_dashboard_toolbar.value } + + + items_order </data> + <data type="text" immediate="true"> + <id>profile_url</id> + <title name="profile_url">Profile URL + URL address of profile page + profile + + + login_url + Login URL + URL address of login page + login + @@ -347,6 +385,11 @@ Mal by panel nástrojov obsahovať menu s výberom jazyka Odhlásenie na paneli nástrojov Mal by panel nástrojov obsahovať tlačidlo na odhlásenie + + URL profilu + URL adresa na stránku profilu + URL prihlásenia + URL adresa na stránku prihlásenia Dashboard-ID @@ -369,6 +412,11 @@ Sollte die Symbolleiste des Dashboards ein Menü mit Sprachauswahl enthalten Abmelden in der Dashboard-Symbolleiste Die Dashboard-Symbolleiste sollte eine Abmeldeschaltfläche enthalten + + Profil-URL + URL-Adresse der Profilseite + Anmelde-URL + URL-Adresse der Anmeldeseite @@ -490,6 +538,34 @@ outline + + profile_url + + visible + + + 0 + 3 + 1 + 2 + + outline + + + + login_url + + visible + + + 2 + 3 + 1 + 2 + + outline + + dashboard_item_list @@ -497,7 +573,7 @@ 0 - 3 + 4 2 2 @@ -511,7 +587,7 @@ 2 - 3 + 4 1 1 @@ -525,7 +601,7 @@ 2 - 4 + 5 1 1 @@ -539,7 +615,7 @@ 3 - 3 + 4 1 1 @@ -553,7 +629,7 @@ 0 - 5 + 6 1 2 @@ -567,7 +643,7 @@ 2 - 5 + 6 1 2 @@ -581,7 +657,7 @@ 0 - 6 + 7 1 4 @@ -595,7 +671,7 @@ 0 - 7 + 8 1 4 From a5e0350f618085c08b622e02921cc7de40cb2b9d Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 13 Feb 2025 13:38:06 +0100 Subject: [PATCH 015/174] [NAE-2052] Integrate ticket view with menu items - implement TabbedTicketView configuration - implement TabbedSingleTaskView configuration - fix missing entry in ViewBody - fix method call in tabbed_case_view_configuration --- .../engine/menu/domain/MenuItemView.java | 6 +- .../configurations/TabbedCaseViewBody.java | 4 - .../TabbedCaseViewConstants.java | 1 - .../TabbedSingleTaskViewBody.java | 31 ++ .../TabbedSingleTaskViewConstants.java | 5 + .../configurations/TabbedTicketViewBody.java | 30 ++ .../TabbedTicketViewConstants.java | 4 + .../menu/domain/configurations/ViewBody.java | 2 + .../domain/configurations/ViewConstants.java | 1 + .../engine-processes/menu/menu_item.xml | 1 + .../menu/tabbed_case_view_configuration.xml | 2 +- .../tabbed_single_task_view_configuration.xml | 319 ++++++++++++++ .../menu/tabbed_ticket_view_configuration.xml | 403 ++++++++++++++++++ 13 files changed, 802 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewConstants.java create mode 100644 src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml create mode 100644 src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 3c7bea1c2ee..5b471452392 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -17,7 +17,11 @@ public enum MenuItemView { TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), true), TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", - "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true); + "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true), + TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of()), "tabbed_ticket_view", + List.of("tabbed_single_task_view"), true), + TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of()), + "tabbed_single_task_view", List.of(), true); private final I18nString name; private final String identifier; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index ca2790c879b..5f18b2920cb 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -69,10 +69,6 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.isHeaderModeChangeable); outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); - if (this.chainedView != null) { - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, - this.chainedView.getViewType().getIdentifier()); - } return outcome; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java index 3fa9d432c4b..b5c8ef2361b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java @@ -18,5 +18,4 @@ public class TabbedCaseViewConstants extends ViewConstants { public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; public static final String FIELD_USE_CASE_DEFAULT_HEADERS = "use_case_default_headers"; - public static final String FIELD_CONFIGURATION_TYPE = "view_configuration_type"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java new file mode 100644 index 00000000000..fa58689c976 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java @@ -0,0 +1,31 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TabbedSingleTaskViewBody extends ViewBody { + private String transitionId; + + @Override + public ViewBody getAssociatedViewBody() { + return null; + } + + @Override + public MenuItemView getViewType() { + return MenuItemView.TABBED_SINGLE_TASK_VIEW; + } + + @Override + protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { + outcome.putDataSetEntry(TabbedSingleTaskViewConstants.FIELD_TRANSITION_ID, FieldType.TEXT, this.transitionId); + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java new file mode 100644 index 00000000000..c0d6949b328 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java @@ -0,0 +1,5 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +public class TabbedSingleTaskViewConstants extends ViewConstants { + public static final String FIELD_TRANSITION_ID = "transition_id"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java new file mode 100644 index 00000000000..f0aafd54b65 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java @@ -0,0 +1,30 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TabbedTicketViewBody extends ViewBody { + + private ViewBody chainedView; + + @Override + public ViewBody getAssociatedViewBody() { + return this.chainedView; + } + + @Override + public MenuItemView getViewType() { + return MenuItemView.TABBED_TICKET_VIEW; + } + + @Override + protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewConstants.java new file mode 100644 index 00000000000..af308eba20c --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewConstants.java @@ -0,0 +1,4 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +public class TabbedTicketViewConstants extends ViewConstants { +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java index 39f07635e1b..d636b6e60b1 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -67,6 +67,8 @@ public ToDataSetOutcome toDataSet(Case associatedViewCase, Case filterCase) { ToDataSetOutcome outcome = new ToDataSetOutcome(); if (associatedViewCase != null) { + outcome.putDataSetEntry(ViewConstants.FIELD_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, + this.getAssociatedViewBody().getViewType().getIdentifier()); outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_ID, FieldType.CASE_REF, List.of(associatedViewCase.getStringId())); String taskId = MenuItemUtils.findTaskIdInCase(associatedViewCase, ViewConstants.TRANS_SETTINGS_ID); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java index 17482b8c2d0..74c38803f06 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java @@ -4,6 +4,7 @@ * Here are declared general constants of menu item configuration processes. */ public class ViewConstants { + public static final String FIELD_CONFIGURATION_TYPE = "view_configuration_type"; public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; public static final String FIELD_VIEW_CONTAINS_FILTER = "contains_filter"; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 5dc42e4a6ad..80e1a7dccfa 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -490,6 +490,7 @@ tabbed_case_view_configuration tabbed_task_view_configuration + tabbed_ticket_view_configuration diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index b33e9b7bbde..81f2ced119b 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -371,7 +371,7 @@ view_configuration_type Pick view type - menuItemService.getAvailableViewsAsOptions(false, useCase.processIdentifier) + menuItemService.getAvailableViewsAsOptions(true, useCase.processIdentifier) view_configuration_type: f.view_configuration_type; diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml new file mode 100644 index 00000000000..561eecda4e0 --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -0,0 +1,319 @@ + + tabbed_single_task_view_configuration + TST + Tabbed single task view configuration + check_box_outline_blank + true + false + false + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + system + System + + + admin + Admin + + + + { + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete, + com.netgrif.application.engine.petrinet.domain.dataset.TaskField previewTaskRef, + com.netgrif.application.engine.petrinet.domain.dataset.CaseField selectedFilterRef, + com.netgrif.application.engine.petrinet.domain.dataset.ButtonField updateBtn, + com.netgrif.application.engine.petrinet.domain.Transition trans + -> + if (filterAutocomplete.getOptions().containsKey(filterAutocomplete.value)) { + change previewTaskRef value { + return [findTask({it.caseId.eq(filterAutocomplete.value).and(it.transitionId.eq("view_filter"))}).stringId] + } + make updateBtn,editable on trans when { true } + } else { + change filterAutocomplete options { + def findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) + && filterCase.dataSet["filter_type"].value == "Case" } + return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") + .findAll(findAllPredicate) + .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) + } + change previewTaskRef value { [] } + make updateBtn,visible on trans when { true } + } + } + + + + + selected_filter_preview + + </data> + <data type="taskRef"> + <id>current_filter_preview</id> + <title/> + </data> + <data type="i18n"> + <id>filter_header</id> + <title/> + <init name="filter_header">Current filter</init> + <component> + <name>divider</name> + </component> + </data> + <data type="text"> + <id>new_filter_id</id> + <title/> + </data> + <data type="boolean" immediate="true"> + <id>contains_filter</id> + <title/> + <init>false</init> + </data> + <data type="enumeration_map"> + <id>filter_autocomplete_selection</id> + <title name="filter_autocomplete_selection">Select new filter + + autocomplete_dynamic + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + + update_filter + + <placeholder name="update_filter">Update view with selected filter</placeholder> + <component> + <name>raised</name> + </component> + <action trigger="set"> + trans: t.settings, + update_filter: f.update_filter, + contains_filter: f.contains_filter, + filter_case: f.filter_case, + filterAutocomplete: f.filter_autocomplete_selection; + + boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" + if (containsFilter) { + def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) + // todo update filterType to your needs + if (filterCase.dataSet["filter_type"].value != "Case") { + throw new IllegalArgumentException("Filter is of wrong type. Only filter of Case type allowed.") + } + } + + change contains_filter value { containsFilter } + change filter_case value { [filterAutocomplete.value] } + change filterAutocomplete value { "" } + make update_filter,visible on trans when { true } + </action> + </data> + <data type="caseRef"> + <id>filter_case</id> + <title/> + <action trigger="set"> + filterTaskRef: f.current_filter_preview, + contains_filter: f.contains_filter, + filterCaseRef: f.filter_case; + + if (filterCaseRef.value == null || filterCaseRef.value == []) { + change filterTaskRef value { [] } + change contains_filter value { false } + return + } + + def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) + change filterTaskRef value { + return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] + } + change contains_filter value { true } + </action> + <allowedNets> + <allowedNet>filter</allowedNet> + </allowedNets> + </data> + + <!-- VIEW CONFIGURATION DATA --> + <data type="i18n"> + <id>view_header</id> + <title/> + <init name="view_header">Single task view</init> + <component> + <name>divider</name> + </component> + </data> + <data type="text"> + <id>transition_id</id> + <title name="transition_id">Transition id + + + + + + Zvoľte nový filter + Aktualizovať zobrazenie s vybraným filtrom + Filter + Súčasný filter + Vybrať zobrazenie + Nastavenie + Asociované zobrazenie + + + + + + Neue Filter auswählen + Filter + Aktueller Filter + Aktualisiere die Ansicht mit dem ausgewählten Filter + Wählen Sie einen Ansichtstyp + Einstellungen + zugehörige Ansicht + + + + + + + initialize + 368 + 208 + + hourglass_empty + + + data_sync + 368 + 328 + + + + settings + 496 + 112 + + settings + + admin + + true + true + true + true + + + + form_title + 4 + grid + + view_header + + visible + + + 0 + 0 + 1 + 4 + + outline + + + + + view_dataGroup + 4 + grid + + transition_id + + editable + + + 0 + 0 + 1 + 4 + + outline + + + + + + initialized + 496 + 208 + + 0 + false + + + uninitialized + 240 + 208 + + 1 + false + + + a1 + read + initialized + settings + 1 + + + a2 + regular + uninitialized + initialize + 1 + + + a3 + regular + initialize + initialized + 1 + + \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml new file mode 100644 index 00000000000..b605d6c82b5 --- /dev/null +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -0,0 +1,403 @@ + + tabbed_ticket_view_configuration + TVC + Tabbed ticket view configuration + check_box_outline_blank + true + false + false + + system + + true + true + true + + + + admin + + true + true + true + + + + default + + false + false + true + + + + + view_delete + + + removeViewCase() + + + + + + system + System + + + admin + Admin + + + + { + com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete, + com.netgrif.application.engine.petrinet.domain.dataset.TaskField previewTaskRef, + com.netgrif.application.engine.petrinet.domain.dataset.CaseField selectedFilterRef, + com.netgrif.application.engine.petrinet.domain.dataset.ButtonField updateBtn, + com.netgrif.application.engine.petrinet.domain.Transition trans + -> + if (filterAutocomplete.getOptions().containsKey(filterAutocomplete.value)) { + change previewTaskRef value { + return [findTask({it.caseId.eq(filterAutocomplete.value).and(it.transitionId.eq("view_filter"))}).stringId] + } + make updateBtn,editable on trans when { true } + } else { + change filterAutocomplete options { + def findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) + && filterCase.dataSet["filter_type"].value == "Case" } + return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") + .findAll(findAllPredicate) + .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) + } + change previewTaskRef value { [] } + make updateBtn,visible on trans when { true } + } + } + + + { -> + + def viewIdAsList = useCase.dataSet['view_configuration_id'].value + if (viewIdAsList == null || viewIdAsList.isEmpty()) { + return + } + + async.run { + workflowService.deleteCase(viewIdAsList[0]) + } + } + + + + + selected_filter_preview + + </data> + <data type="taskRef"> + <id>current_filter_preview</id> + <title/> + </data> + <data type="i18n"> + <id>filter_header</id> + <title/> + <init name="filter_header">Current filter</init> + <component> + <name>divider</name> + </component> + </data> + <data type="text"> + <id>new_filter_id</id> + <title/> + </data> + <data type="boolean" immediate="true"> + <id>contains_filter</id> + <title/> + <init>false</init> + </data> + <data type="enumeration_map"> + <id>filter_autocomplete_selection</id> + <title name="filter_autocomplete_selection">Select new filter + + autocomplete_dynamic + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + trans: t.settings, + filterAutocomplete: f.this, + filter_case: f.filter_case, + update_filter: f.update_filter, + previewTaskRef: f.selected_filter_preview; + + updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) + + + + update_filter + + <placeholder name="update_filter">Update view with selected filter</placeholder> + <component> + <name>raised</name> + </component> + <action trigger="set"> + trans: t.settings, + update_filter: f.update_filter, + contains_filter: f.contains_filter, + filter_case: f.filter_case, + filterAutocomplete: f.filter_autocomplete_selection; + + boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" + if (containsFilter) { + def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) + if (filterCase.dataSet["filter_type"].value != "Case") { + throw new IllegalArgumentException("Filter is of wrong type. Only filter of Case type allowed.") + } + } + + change contains_filter value { containsFilter } + change filter_case value { [filterAutocomplete.value] } + change filterAutocomplete value { "" } + make update_filter,visible on trans when { true } + </action> + </data> + <data type="caseRef"> + <id>filter_case</id> + <title/> + <action trigger="set"> + filterTaskRef: f.current_filter_preview, + contains_filter: f.contains_filter, + filterCaseRef: f.filter_case; + + if (filterCaseRef.value == null || filterCaseRef.value == []) { + change filterTaskRef value { [] } + change contains_filter value { false } + return + } + + def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) + change filterTaskRef value { + return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] + } + change contains_filter value { true } + </action> + <allowedNets> + <allowedNet>filter</allowedNet> + </allowedNets> + </data> + + <!-- ASSOCIATED VIEW: this section can be removed if needed --> + <data type="enumeration_map" immediate="true"> + <id>view_configuration_type</id> + <title name="view_configuration_type">Pick view type + + menuItemService.getAvailableViewsAsOptions(true, useCase.processIdentifier) + + + view_configuration_type: f.view_configuration_type; + + if (view_configuration_type.options == null || view_configuration_type.options.isEmpty()) { + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(true) } + } + + + + view_configuration_id + + <allowedNets> + <allowedNet>tabbed_single_task_view_configuration</allowedNet> + </allowedNets> + </data> + <data type="taskRef"> + <id>view_configuration_form</id> + <title/> + </data> + <!-- END OF ASSOCIATED VIEW --> + + <!-- VIEW CONFIGURATION DATA --> + <data type="i18n"> + <id>view_header</id> + <title/> + <init name="view_header">Ticket view</init> + <component> + <name>divider</name> + </component> + </data> + <!-- END OF VIEW CONFIGURATION DATA --> + + <!-- I18NS --> + <i18n locale="sk"> + <i18nString name="filter_autocomplete_selection">Zvoľte nový filter</i18nString> + <i18nString name="update_filter">Aktualizovať zobrazenie s vybraným filtrom</i18nString> + <i18nString name="filter_update_title">Filter</i18nString> + <i18nString name="filter_header">Súčasný filter</i18nString> + <i18nString name="view_configuration_type">Vybrať zobrazenie</i18nString> + <i18nString name="settings">Nastavenie</i18nString> + <i18nString name="associated_view">Asociované zobrazenie</i18nString> + <!-- todo translate --> + <i18nString name="view_header"></i18nString> + </i18n> + <i18n locale="de"> + <i18nString name="filter_autocomplete_selection">Neue Filter auswählen</i18nString> + <i18nString name="filter_update_title">Filter</i18nString> + <i18nString name="filter_header">Aktueller Filter</i18nString> + <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> + <i18nString name="view_configuration_type">Wählen Sie einen Ansichtstyp</i18nString> + <i18nString name="settings">Einstellungen</i18nString> + <i18nString name="associated_view">zugehörige Ansicht</i18nString> + <!-- todo translate --> + <i18nString name="view_header"></i18nString> + </i18n> + + <transition> + <id>initialize</id> + <x>368</x> + <y>208</y> + <label>initialize [await sync]</label> + <icon>hourglass_empty</icon> + </transition> + <transition> + <id>data_sync</id> + <x>368</x> + <y>328</y> + <label>Data sync</label> + </transition> + <transition> + <id>settings</id> + <x>496</x> + <y>112</y> + <label name="settings">Settings</label> + <icon>settings</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + <view>true</view> + <cancel>true</cancel> + <assign>true</assign> + </logic> + </roleRef> + <dataGroup> + <id>form_title</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>view_header</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + <dataGroup> + <id>associated_view</id> + <cols>4</cols> + <layout>grid</layout> + <title name="associated_view">Associated view + + view_configuration_type + + editable + + + 0 + 0 + 1 + 4 + 0 + + outline + + + 0 + + + view_configuration_type: f.view_configuration_type, + view_configuration_form: f.view_configuration_form, + view_configuration_id: f.view_configuration_id; + + if (view_configuration_type.value == null || view_configuration_type.value == "") { + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + return + } + + def configurationCase = createCase(view_configuration_type.value + "_configuration") + def initTask = assignTask("initialize", configurationCase) + finishTask(initTask) + change view_configuration_id value { [configurationCase.stringId] } + change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + + + + + + view_configuration_form + + editable + + + 0 + 1 + 1 + 4 + 0 + + outline + + + + + + initialized + 496 + 208 + + 0 + false + + + uninitialized + 240 + 208 + + 1 + false + + + a1 + read + initialized + settings + 1 + + + a2 + regular + uninitialized + initialize + 1 + + + a3 + regular + initialize + initialized + 1 + + \ No newline at end of file From 1097ea349a142341632133c58141602d4e88f3b5 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Thu, 13 Feb 2025 14:25:10 +0100 Subject: [PATCH 016/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - change default value of login url to empty string --- .../petriNets/engine-processes/dashboard_management.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml index ddf56972c8c..2857243b816 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_management.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -359,8 +359,7 @@ login_url Login URL - URL address of login page - login + URL address of login page. Let empty to decide by application. @@ -389,7 +388,7 @@ URL profilu URL adresa na stránku profilu URL prihlásenia - URL adresa na stránku prihlásenia + URL adresa na stránku prihlásenia. Nechajte prázdne pre rozhodnutie aplikáciou. Dashboard-ID @@ -416,7 +415,7 @@ Profil-URL URL-Adresse der Profilseite Anmelde-URL - URL-Adresse der Anmeldeseite + URL-Adresse der Anmeldeseite. Lassen Sie leer, um durch Anwendung zu entscheiden. From 9dc653eab3541a2a5a61eeb669ec5b759d432d89 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Thu, 13 Feb 2025 18:04:54 +0100 Subject: [PATCH 017/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update by PR comments - create findCasesElastic method in ActionDelegate with default page size of 1000 items - check duplicate ids in dashboard_item and dashboard_management in finish action - move importProcess function into ImportHelper and clean runners --- .../logic/action/ActionDelegate.groovy | 9 ++++ .../startup/DashboardManagementRunner.groovy | 38 +------------- .../engine/startup/DashboardRunner.groovy | 38 +------------- .../engine/startup/FilterRunner.groovy | 50 ++----------------- .../engine/startup/ImpersonationRunner.groovy | 31 +----------- .../engine/startup/ImportHelper.groovy | 19 +++++++ .../engine-processes/dashboard_item.xml | 37 ++++++-------- .../engine-processes/dashboard_management.xml | 41 +++++++-------- 8 files changed, 72 insertions(+), 191 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index f981a49dcaa..c0dedf2e1c8 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2472,6 +2472,15 @@ class ActionDelegate { return result ? result[0] : null } + /** + * search elastic with string query for cases and default page size of 1000 cases + * @param query + * @return + */ + List findCasesElastic(String query, int pageSize = 1000) { + this.findCasesElastic(query, PageRequest.of(0, pageSize)) + } + /** * search elastic with string query for cases * @param query diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy index 7d9a2f74c78..a95e79215ce 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/DashboardManagementRunner.groovy @@ -1,8 +1,5 @@ package com.netgrif.application.engine.startup -import com.netgrif.application.engine.petrinet.domain.PetriNet -import com.netgrif.application.engine.petrinet.domain.VersionType -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty @@ -13,15 +10,9 @@ import org.springframework.stereotype.Component @ConditionalOnProperty(value = "nae.dashboard-management.enabled", matchIfMissing = true) class DashboardManagementRunner extends AbstractOrderedCommandLineRunner { - @Autowired - private IPetriNetService petriNetService - @Autowired private ImportHelper helper - @Autowired - private SystemUserRunner systemCreator - public static final String DASHBOARD_MANAGEMENT_NET_IDENTIFIER = "dashboard_management" private static final String DASHBOARD_MANAGEMENT_FILE_NAME = "engine-processes/dashboard_management.xml" @@ -30,32 +21,7 @@ class DashboardManagementRunner extends AbstractOrderedCommandLineRunner { @Override void run(String... args) throws Exception { - createDashboardManagementNet() - createDashboardItemNet() - } - - Optional createDashboardManagementNet() { - importProcess("Petri net for filters", DASHBOARD_MANAGEMENT_NET_IDENTIFIER, DASHBOARD_MANAGEMENT_FILE_NAME) - } - - Optional createDashboardItemNet() { - importProcess("Petri net for filter preferences", DASHBOARD_ITEM_NET_IDENTIFIER, DASHBOARD_ITEM_FILE_NAME) - } - - - Optional importProcess(String message, String netIdentifier, String netFileName) { - PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) - if (filter != null) { - log.info("${message} has already been imported.") - return Optional.of(filter) - } - - Optional filterNet = helper.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) - - if (!filterNet.isPresent()) { - log.error("Import of ${message} failed!") - } - - return filterNet + helper.importProcess("Petri net for filters", DASHBOARD_MANAGEMENT_NET_IDENTIFIER, DASHBOARD_MANAGEMENT_FILE_NAME) + helper.importProcess("Petri net for filter preferences", DASHBOARD_ITEM_NET_IDENTIFIER, DASHBOARD_ITEM_FILE_NAME) } } diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DashboardRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DashboardRunner.groovy index 4c3690da982..aeafd8a270b 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/DashboardRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/DashboardRunner.groovy @@ -1,8 +1,5 @@ package com.netgrif.application.engine.startup -import com.netgrif.application.engine.petrinet.domain.PetriNet -import com.netgrif.application.engine.petrinet.domain.VersionType -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty @@ -13,15 +10,9 @@ import org.springframework.stereotype.Component @ConditionalOnProperty(value = "nae.dashboard.enabled", matchIfMissing = false) class DashboardRunner extends AbstractOrderedCommandLineRunner { - @Autowired - private IPetriNetService petriNetService - @Autowired private ImportHelper helper - @Autowired - private SystemUserRunner systemCreator - public static final String DASHBOARD_NET_IDENTIFIER = "dashboard" private static final String DASHBOARD_FILE_NAME = "engine-processes/dashboard.xml" @@ -30,32 +21,7 @@ class DashboardRunner extends AbstractOrderedCommandLineRunner { @Override void run(String... args) throws Exception { - createDashboardNet() - createDashboardTileNet() - } - - Optional createDashboardNet() { - importProcess("Petri net for filters", DASHBOARD_NET_IDENTIFIER, DASHBOARD_FILE_NAME) - } - - Optional createDashboardTileNet() { - importProcess("Petri net for filter preferences", DASHBOARD_TILE_NET_IDENTIFIER, DASHBOARD_TILE_FILE_NAME) - } - - - Optional importProcess(String message, String netIdentifier, String netFileName) { - PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) - if (filter != null) { - log.info("${message} has already been imported.") - return Optional.of(filter) - } - - Optional filterNet = helper.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) - - if (!filterNet.isPresent()) { - log.error("Import of ${message} failed!") - } - - return filterNet + helper.importProcess("Petri net for filters", DASHBOARD_NET_IDENTIFIER, DASHBOARD_FILE_NAME) + helper.importProcess("Petri net for filter preferences", DASHBOARD_TILE_NET_IDENTIFIER, DASHBOARD_TILE_FILE_NAME) } } diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index 0e1608c6da4..1db0d4b97bf 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -1,9 +1,5 @@ package com.netgrif.application.engine.startup -import com.netgrif.application.engine.petrinet.domain.PetriNet -import com.netgrif.application.engine.petrinet.domain.VersionType -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService -import com.netgrif.application.engine.workflow.domain.eventoutcomes.petrinetoutcomes.ImportPetriNetEventOutcome import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @@ -12,15 +8,9 @@ import org.springframework.stereotype.Component @Component class FilterRunner extends AbstractOrderedCommandLineRunner { - @Autowired - private IPetriNetService petriNetService - @Autowired private ImportHelper helper - @Autowired - private SystemUserRunner systemCreator - private static final String FILTER_FILE_NAME = "engine-processes/filter.xml" public static final String FILTER_PETRI_NET_IDENTIFIER = "filter" @@ -35,41 +25,9 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { @Override void run(String... args) throws Exception { - createFilterNet() - createPreferenceItemNet() - createImportFiltersNet() - createExportFiltersNet() - } - - Optional createFilterNet() { - importProcess("Petri net for filters", FILTER_PETRI_NET_IDENTIFIER, FILTER_FILE_NAME) - } - - Optional createPreferenceItemNet() { - importProcess("Petri net for filter preferences", PREFERRED_ITEM_NET_IDENTIFIER, PREFERRED_ITEM_FILE_NAME) - } - - Optional createImportFiltersNet() { - importProcess("Petri net for importing filters", IMPORT_NET_IDENTIFIER, IMPORT_FILTER_FILE_NAME) - } - - Optional createExportFiltersNet() { - importProcess("Petri net for exporting filters", EXPORT_NET_IDENTIFIER, EXPORT_FILTER_FILE_NAME) - } - - Optional importProcess(String message, String netIdentifier, String netFileName) { - PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) - if (filter != null) { - log.info("${message} has already been imported.") - return Optional.of(filter) - } - - Optional filterNet = helper.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) - - if (!filterNet.isPresent()) { - log.error("Import of ${message} failed!") - } - - return filterNet + helper.importProcess("Petri net for filters", FILTER_PETRI_NET_IDENTIFIER, FILTER_FILE_NAME) + helper.importProcess("Petri net for filter preferences", PREFERRED_ITEM_NET_IDENTIFIER, PREFERRED_ITEM_FILE_NAME) + helper.importProcess("Petri net for importing filters", IMPORT_NET_IDENTIFIER, IMPORT_FILTER_FILE_NAME) + helper.importProcess("Petri net for exporting filters", EXPORT_NET_IDENTIFIER, EXPORT_FILTER_FILE_NAME) } } diff --git a/src/main/groovy/com/netgrif/application/engine/startup/ImpersonationRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/ImpersonationRunner.groovy index d2766a4e413..93e07d36550 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/ImpersonationRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/ImpersonationRunner.groovy @@ -1,8 +1,5 @@ package com.netgrif.application.engine.startup -import com.netgrif.application.engine.petrinet.domain.PetriNet -import com.netgrif.application.engine.petrinet.domain.VersionType -import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @@ -11,14 +8,9 @@ import org.springframework.stereotype.Component @Component class ImpersonationRunner extends AbstractOrderedCommandLineRunner { - @Autowired - protected IPetriNetService petriNetService - @Autowired protected ImportHelper helper - @Autowired - protected SystemUserRunner systemCreator protected static final String IMPERSONATION_CONFIG_FILE_NAME = "engine-processes/impersonation_config.xml" public static final String IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER = "impersonation_config" @@ -28,26 +20,7 @@ class ImpersonationRunner extends AbstractOrderedCommandLineRunner { @Override void run(String... args) throws Exception { - createConfigNets() - } - - void createConfigNets() { - importProcess("Petri net for impersonation config", IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER, IMPERSONATION_CONFIG_FILE_NAME) - importProcess("Petri net for impersonation user select", IMPERSONATION_CONFIG_USER_SELECT_PETRI_NET_IDENTIFIER, IMPERSONATION_CONFIG_USER_SELECT_FILE_NAME) - } - - Optional importProcess(String message, String netIdentifier, String netFileName) { - PetriNet foundNet = petriNetService.getNewestVersionByIdentifier(netIdentifier) - if (foundNet != null) { - log.info("${message} has already been imported.") - return Optional.of(foundNet) - } - - Optional net = helper.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) - if (!net.isPresent()) { - log.error("Import of ${message} failed!") - } - - return net + helper.importProcess("Petri net for impersonation config", IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER, IMPERSONATION_CONFIG_FILE_NAME) + helper.importProcess("Petri net for impersonation user select", IMPERSONATION_CONFIG_USER_SELECT_PETRI_NET_IDENTIFIER, IMPERSONATION_CONFIG_USER_SELECT_FILE_NAME) } } diff --git a/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy b/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy index 8b7de95626b..295c23f9f9f 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy @@ -90,6 +90,9 @@ class ImportHelper { @Autowired private IUriService uriService + @Autowired + private SystemUserRunner systemCreator + private final ClassLoader loader = ImportHelper.getClassLoader() @@ -239,6 +242,22 @@ class ImportHelper { superCreator.setAllToSuperUser(); } + Optional importProcess(String message, String netIdentifier, String netFileName) { + PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) + if (filter != null) { + log.info("${message} has already been imported.") + return Optional.of(filter) + } + + Optional filterNet = this.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) + + if (!filterNet.isPresent()) { + log.error("Import of ${message} failed!") + } + + return filterNet + } + static ObjectNode populateDataset(Map> data) { ObjectMapper mapper = new ObjectMapper() String json = mapper.writeValueAsString(data) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index 18aae62b294..d73b04e8dc6 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -23,14 +23,6 @@ true - - default - - true - true - true - - system @@ -54,9 +46,8 @@ if (item_id.value == "") { return } - def itemWithIdExists = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\"" as String, org.springframework.data.domain.PageRequest.of(0, 1)).isEmpty() - if (!itemWithIdExists) { - change item_id value { "" } + def itemWithIdExists = findCaseElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\"" as String) + if (itemWithIdExists != null) { throw new IllegalArgumentException("Dashboard item with given ID already exists.") } @@ -76,7 +67,7 @@ preference_items_list: f.preference_items_list; - def preferenceItemCases = findCasesElastic("processIdentifier:\"preference_item\"" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + def preferenceItemCases = findCasesElastic("processIdentifier:\"preference_item\"" as String) .collectEntries { [(it.stringId): it.dataSet["menu_name"].value] } change preference_items_list options { preferenceItemCases } @@ -426,15 +417,15 @@ tune auto - - - - - - - - - + + admin + + true + true + true + true + + configuration_d1 4 @@ -689,6 +680,10 @@ if (item_id.value == null || item_id.value == "") { throw new IllegalArgumentException("Dashboard item ID cannot be empty.") } + def itemWithIdExists = findCaseElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\"" as String) + if (itemWithIdExists != null) { + throw new IllegalArgumentException("Dashboard item with given ID already exists.") + } if (is_internal.value) { if (preference_items_list.value == null || !org.bson.types.ObjectId.isValid(preference_items_list.value)){ throw new IllegalArgumentException("Internal menu item must be selected.") diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml index 2857243b816..575e23eb537 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_management.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -23,14 +23,6 @@ true - - default - - true - true - true - - system @@ -54,9 +46,8 @@ if (dashboard_id.value == "") { return } - def dashboardWithIdExists = findCasesElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\"" as String, org.springframework.data.domain.PageRequest.of(0, 1)).isEmpty() - if (!dashboardWithIdExists) { - change dashboard_id value { "" } + def dashboardWithIdExists = findCaseElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\"" as String) + if (dashboardWithIdExists != null) { throw new IllegalArgumentException("Dashboard with given ID already exists.") } @@ -176,7 +167,7 @@ dashboard_item_list: f.dashboard_item_list, existing_menu_items: f.existing_menu_items; - def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) change existing_menu_items options { dashboardItemCases } @@ -234,7 +225,7 @@ } change dashboard_item_to_preference_item options { preferenceItemOptions } - def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) change existing_menu_items options { dashboardItemCases } @@ -268,7 +259,7 @@ preferenceItemOptions.keySet().removeAll([dashboard_item_list.value]) change dashboard_item_to_preference_item options { preferenceItemOptions } - def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String, org.springframework.data.domain.PageRequest.of(0, 1000)) + def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } dashboardItemCases.keySet().removeAll(dashboard_item_list.options.keySet()) change existing_menu_items options { dashboardItemCases } @@ -426,15 +417,15 @@ settings auto - - - - - - - - - + + admin + + true + true + true + true + + configuration_d1 4 @@ -699,6 +690,10 @@ if (dashboard_id.value == null || dashboard_id.value == "") { throw new IllegalArgumentException("Dashboard ID cannot be empty.") } + def dashboardWithIdExists = findCaseElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\"" as String) + if (dashboardWithIdExists != null) { + throw new IllegalArgumentException("Dashboard with given ID already exists.") + } change is_active value { true } From dd06c6ebbcb3c919a4c3e9c0ed19853d1e794748 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 14 Feb 2025 10:17:08 +0100 Subject: [PATCH 018/174] [NAE-2052] Integrate ticket view with menu items - fix setting view_configuration_type --- .../petriNets/engine-processes/menu/menu_item.xml | 10 +++++++--- .../menu/tabbed_case_view_configuration.xml | 10 +++++++--- .../menu/tabbed_ticket_view_configuration.xml | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 80e1a7dccfa..f47c948f8d3 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -1114,16 +1114,20 @@ view_configuration_form: f.view_configuration_form, view_configuration_id: f.view_configuration_id; + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + if (view_configuration_type.value == null || view_configuration_type.value == "") { - if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { - workflowService.deleteCase(view_configuration_id.value[0]) - } + change view_configuration_id value { [] } + change view_configuration_form value { [] } return } def configurationCase = createCase(view_configuration_type.value + "_configuration") def initTask = assignTask("initialize", configurationCase) finishTask(initTask) + configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_id value { [configurationCase.stringId] } change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 81f2ced119b..57908c31a62 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -859,16 +859,20 @@ view_configuration_form: f.view_configuration_form, view_configuration_id: f.view_configuration_id; + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + if (view_configuration_type.value == null || view_configuration_type.value == "") { - if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { - workflowService.deleteCase(view_configuration_id.value[0]) - } + change view_configuration_id value { [] } + change view_configuration_form value { [] } return } def configurationCase = createCase(view_configuration_type.value + "_configuration") def initTask = assignTask("initialize", configurationCase) finishTask(initTask) + configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_id value { [configurationCase.stringId] } change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index b605d6c82b5..d3fada29aef 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -330,10 +330,13 @@ view_configuration_form: f.view_configuration_form, view_configuration_id: f.view_configuration_id; + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + if (view_configuration_type.value == null || view_configuration_type.value == "") { - if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { - workflowService.deleteCase(view_configuration_id.value[0]) - } + change view_configuration_id value { [] } + change view_configuration_form value { [] } return } @@ -341,6 +344,7 @@ def initTask = assignTask("initialize", configurationCase) finishTask(initTask) change view_configuration_id value { [configurationCase.stringId] } + configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } From 7971f6d5224acbaab8150728f1fd9ddd959b09a0 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 14 Feb 2025 10:25:53 +0100 Subject: [PATCH 019/174] [NAE-2052] Integrate ticket view with menu items - add translations --- .../engine/menu/domain/MenuItemView.java | 7 ++++--- .../tabbed_single_task_view_configuration.xml | 15 ++++++--------- .../menu/tabbed_ticket_view_configuration.xml | 6 ++---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 5b471452392..68af7cda5c3 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -18,10 +18,11 @@ public enum MenuItemView { "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), true), TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true), - TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of()), "tabbed_ticket_view", + TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", + "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", List.of("tabbed_single_task_view"), true), - TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of()), - "tabbed_single_task_view", List.of(), true); + TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", + "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true); private final I18nString name; private final String identifier; diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 561eecda4e0..10c34b150e8 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -134,9 +134,8 @@ boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" if (containsFilter) { def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) - // todo update filterType to your needs - if (filterCase.dataSet["filter_type"].value != "Case") { - throw new IllegalArgumentException("Filter is of wrong type. Only filter of Case type allowed.") + if (filterCase.dataSet["filter_type"].value != "Task") { + throw new IllegalArgumentException("Filter is of wrong type. Only filter of Task type allowed.") } } @@ -195,9 +194,8 @@ Vybrať zobrazenie Nastavenie Asociované zobrazenie - - - + Zobrazenie jednej úlohy + ID prechodu Neue Filter auswählen @@ -207,9 +205,8 @@ Wählen Sie einen Ansichtstyp Einstellungen zugehörige Ansicht - - - + Einzelaufgabenansicht + Übergangs-ID diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index d3fada29aef..7608ab2b274 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -241,8 +241,7 @@ Vybrať zobrazenie Nastavenie Asociované zobrazenie - - + Tiketové zobrazenie Neue Filter auswählen @@ -252,8 +251,7 @@ Wählen Sie einen Ansichtstyp Einstellungen zugehörige Ansicht - - + Ticketansicht From 817cb99950cc31c8d8a8c09ccc21cf2ddbeeeac6 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Fri, 14 Feb 2025 10:58:33 +0100 Subject: [PATCH 020/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - documentation --- .../logic/action/ActionDelegate.groovy | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index c0dedf2e1c8..26985d829ed 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2464,8 +2464,8 @@ class ActionDelegate { /** * search elastic with string query for first occurrence - * @param query - * @return + * @param query string with search conditions + * @return one case which match search condition or null */ Case findCaseElastic(String query) { def result = findCasesElastic(query, PageRequest.of(0, 1)) @@ -2474,8 +2474,9 @@ class ActionDelegate { /** * search elastic with string query for cases and default page size of 1000 cases - * @param query - * @return + * @param query string with search conditions + * @param pageSize optional parameter which decides number of returned elements + * @return list of cases (default max 1000) which match condition */ List findCasesElastic(String query, int pageSize = 1000) { this.findCasesElastic(query, PageRequest.of(0, pageSize)) @@ -2483,8 +2484,9 @@ class ActionDelegate { /** * search elastic with string query for cases - * @param query - * @return + * @param query string with search conditions + * @param pageable object which decides page size, page number and order of elements + * @return list of cases (size and order depends on pageable object) which match condition */ List findCasesElastic(String query, Pageable pageable) { CaseSearchRequest request = new CaseSearchRequest() @@ -2493,6 +2495,11 @@ class ActionDelegate { return result } + /** + * find count of cases which match condition + * @param query string with search conditions + * @return number of cases which match condition + */ long countCasesElastic(String query) { CaseSearchRequest request = new CaseSearchRequest() request.query = query From be3da230bc00c8afb1e92b3c957e568142001fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Fri, 14 Feb 2025 11:51:54 +0100 Subject: [PATCH 021/174] [NAE-2039] Search in workflow view - implement basic elastic search for title and identifier to ElasticPetriNetService and implement endpoint for elastic search --- .../service/ElasticPetriNetService.java | 123 +++++++++++++++++- .../interfaces/IElasticPetriNetService.java | 8 ++ .../petrinet/web/PetriNetController.java | 18 +++ 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java index 3777f1ddb86..b51ccc432d4 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java @@ -1,20 +1,40 @@ package com.netgrif.application.engine.elastic.service; +import com.netgrif.application.engine.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.domain.ElasticPetriNet; import com.netgrif.application.engine.elastic.domain.ElasticPetriNetRepository; + import com.netgrif.application.engine.elastic.service.executors.Executor; import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetService; import com.netgrif.application.engine.petrinet.domain.PetriNet; +import com.netgrif.application.engine.petrinet.domain.PetriNetSearch; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.petrinet.web.responsebodies.PetriNetReference; +import com.netgrif.application.engine.utils.FullPageRequest; import lombok.extern.slf4j.Slf4j; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.SearchHitSupport; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Service; -import java.util.List; +import java.util.*; +import java.util.function.BinaryOperator; import java.util.stream.Collectors; +import static org.elasticsearch.index.query.QueryBuilders.*; + @Service @Slf4j public class ElasticPetriNetService implements IElasticPetriNetService { @@ -25,9 +45,15 @@ public class ElasticPetriNetService implements IElasticPetriNetService { private IPetriNetService petriNetService; - public ElasticPetriNetService(ElasticPetriNetRepository repository, Executor executors) { + private final ElasticsearchRestTemplate template; + + @Value("${spring.data.elasticsearch.index.petrinet}") + protected String netIndex; + + public ElasticPetriNetService(ElasticPetriNetRepository repository, Executor executors, ElasticsearchRestTemplate template) { this.repository = repository; this.executors = executors; + this.template = template; } @Lazy @@ -89,4 +115,97 @@ public List findAllByUriNodeId(String uriNodeId) { List elasticPetriNets = repository.findAllByUriNodeId(uriNodeId); return petriNetService.findAllById(elasticPetriNets.stream().map(ElasticPetriNet::getStringId).collect(Collectors.toList())); } + + @Override + public Page search(PetriNetSearch requests, LoggedUser user, Pageable pageable, Locale locale, Boolean isIntersection) { + if (requests == null) { + throw new IllegalArgumentException("Request can not be null!"); + } + log.debug("Searching for PetriNet query with logged user [{}]", user.getId()); + LoggedUser loggedOrImpersonated = user.getSelfOrImpersonated(); + NativeSearchQuery query = buildQuery(requests, loggedOrImpersonated, pageable, locale, isIntersection); + List netPage; + long total; + if (query != null) { + SearchHits hits = template.search(query, ElasticPetriNet.class, IndexCoordinates.of(netIndex)); + Page indexedNets = (Page) SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(hits, query.getPageable())); + netPage = petriNetService.findAllById(indexedNets.get().map(ElasticPetriNet::getStringId).collect(Collectors.toList())); + total = indexedNets.getTotalElements(); + log.debug("Found [{}] total elements of page [{}]", netPage.size(), pageable.getPageNumber()); + } else { + netPage = Collections.emptyList(); + total = 0; + } + + return new PageImpl<>(netPage.stream().map(net -> new PetriNetReference(net, locale)).collect(Collectors.toList()), pageable, total); + } + + protected NativeSearchQuery buildQuery(PetriNetSearch request, LoggedUser user, Pageable pageable, Locale locale, Boolean isIntersection) { + List singleQueries = new LinkedList<>(); + singleQueries.add(buildSingleQuery(request, user, locale)); + + if (isIntersection && !singleQueries.stream().allMatch(Objects::nonNull)) { + // one of the queries evaluates to empty set => the entire result is an empty set + return null; + } else if (!isIntersection) { + singleQueries = singleQueries.stream().filter(Objects::nonNull).collect(Collectors.toList()); + if (singleQueries.size() == 0) { + // all queries result in an empty set => the entire result is an empty set + return null; + } + } + + BinaryOperator reductionOperator = isIntersection ? BoolQueryBuilder::must : BoolQueryBuilder::should; + BoolQueryBuilder query = singleQueries.stream().reduce(new BoolQueryBuilder(), reductionOperator); + + NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); + return builder + .withQuery(query) + .withPageable(pageable) + .build(); + } + + protected BoolQueryBuilder buildSingleQuery(PetriNetSearch request, LoggedUser user, Locale locale) { + BoolQueryBuilder query = boolQuery(); + + buildFullTextQuery(request, query); + // TODO: NAE-2039 check group + boolean resultAlwaysEmpty = buildGroupQuery(request, user, locale, query); + if (resultAlwaysEmpty) { + return null; + } + return query; + } + + protected void buildFullTextQuery(PetriNetSearch request, BoolQueryBuilder query) { + if (request.getTitle() == null || request.getTitle().isEmpty()) { + return; + } + + String searchText = "*" + request.getTitle() + "*"; + Map fields = new HashMap<>(); + fields.put("title.defaultValue", 2f); + fields.put("identifier", 1f); + QueryBuilder fullTextQuery = queryStringQuery(searchText).fields(fields); + query.must(fullTextQuery); + } + + protected boolean buildGroupQuery(PetriNetSearch request, LoggedUser user, Locale locale, BoolQueryBuilder query) { + if (request.getGroup() == null || request.getGroup().isEmpty()) { + return false; + } + + PetriNetSearch processQuery = new PetriNetSearch(); + processQuery.setGroup(request.getGroup()); + List groupProcesses = this.petriNetService.search(processQuery, user, new FullPageRequest(), locale).getContent(); + if (groupProcesses.size() == 0) + return true; + + BoolQueryBuilder groupQuery = boolQuery(); + groupProcesses.stream().map(PetriNetReference::getIdentifier) + .map(netIdentifier -> termQuery("identifier", netIdentifier)) + .forEach(groupQuery::should); + query.filter(groupQuery); + return false; + } } diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java b/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java index 6be7daaf0a3..54a2c96abe9 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java @@ -1,10 +1,16 @@ package com.netgrif.application.engine.elastic.service.interfaces; +import com.netgrif.application.engine.auth.domain.LoggedUser; import com.netgrif.application.engine.elastic.domain.ElasticPetriNet; import com.netgrif.application.engine.petrinet.domain.PetriNet; +import com.netgrif.application.engine.petrinet.domain.PetriNetSearch; +import com.netgrif.application.engine.petrinet.web.responsebodies.PetriNetReference; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.scheduling.annotation.Async; import java.util.List; +import java.util.Locale; public interface IElasticPetriNetService { @@ -19,4 +25,6 @@ public interface IElasticPetriNetService { List findAllByUriNodeId(String uriNodeId); + Page search(PetriNetSearch requests, LoggedUser user, Pageable pageable, Locale locale, Boolean isIntersection); + } diff --git a/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java index 4b983dcf1c1..ed29ce7f6a6 100644 --- a/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java +++ b/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.AsyncRunner; import com.netgrif.application.engine.auth.domain.LoggedUser; +import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetService; import com.netgrif.application.engine.eventoutcomes.LocalisedEventOutcomeFactory; import com.netgrif.application.engine.importer.service.Importer; import com.netgrif.application.engine.importer.service.throwable.MissingIconKeyException; @@ -71,6 +72,9 @@ public class PetriNetController { @Autowired private IPetriNetService service; + @Autowired + private IElasticPetriNetService elasticService; + @Autowired private IProcessRoleService roleService; @@ -197,6 +201,20 @@ PagedModel searchPetriNets(@RequestBody PetriNetSearc return resources; } + @Operation(summary = "Search elastic processes", security = {@SecurityRequirement(name = "BasicAuth")}) + @PostMapping(value = "/search_elastic", produces = MediaTypes.HAL_JSON_VALUE) + public @ResponseBody + PagedModel searchElasticPetriNets(@RequestBody PetriNetSearch criteria, Authentication auth, Pageable pageable, PagedResourcesAssembler assembler, Locale locale) { + LoggedUser user = (LoggedUser) auth.getPrincipal(); + // TODO: add Merge Filters and its operations + Page nets = elasticService.search(criteria, user, pageable, locale,false); + Link selfLink = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(PetriNetController.class) + .searchElasticPetriNets(criteria, auth, pageable, assembler, locale)).withRel("search_elastic"); + PagedModel resources = assembler.toModel(nets, new PetriNetReferenceResourceAssembler(), selfLink); + PetriNetReferenceResourceAssembler.buildLinks(resources); + return resources; + } + @PreAuthorize("@petriNetAuthorizationService.canCallProcessDelete(#auth.getPrincipal(), #processId)") @Operation(summary = "Delete process", description = "Caller must have the ADMIN role. Removes the specified process, along with it's cases, tasks and process roles.", From e3f80abef063124df34d2055b81f17038bd551a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Fri, 14 Feb 2025 11:52:55 +0100 Subject: [PATCH 022/174] [NAE-2039] Search in workflow view - remove TODO --- .../engine/elastic/service/ElasticPetriNetService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java index b51ccc432d4..30d4eb3b056 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java @@ -169,7 +169,6 @@ protected BoolQueryBuilder buildSingleQuery(PetriNetSearch request, LoggedUser u BoolQueryBuilder query = boolQuery(); buildFullTextQuery(request, query); - // TODO: NAE-2039 check group boolean resultAlwaysEmpty = buildGroupQuery(request, user, locale, query); if (resultAlwaysEmpty) { return null; From 145e3e06e01b6c367b856ab6d5bc6d8ccb7d1188 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 14 Feb 2025 12:32:56 +0100 Subject: [PATCH 023/174] [NAE-2052] Integrate ticket view with menu items - enabled MenuItemApiTest --- .../netgrif/application/engine/action/MenuItemApiTest.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index bbd721fb593..e3ae062e5e0 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -1,6 +1,5 @@ package com.netgrif.application.engine.action - import com.netgrif.application.engine.TestHelper import com.netgrif.application.engine.auth.service.interfaces.IUserService import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService @@ -23,7 +22,6 @@ import com.netgrif.application.engine.workflow.service.interfaces.IDataService import com.netgrif.application.engine.workflow.service.interfaces.ITaskService import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired @@ -36,7 +34,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension import static org.junit.jupiter.api.Assertions.assertThrows -@Disabled @SpringBootTest @ActiveProfiles(["test"]) @ExtendWith(SpringExtension.class) From 1c16250cbe07f4f062558d6db0a6c505f3508b30 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Fri, 14 Feb 2025 13:30:09 +0100 Subject: [PATCH 024/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - change default page size to 100 in findCasesElastic --- .../domain/dataset/logic/action/ActionDelegate.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 26985d829ed..1a29eb05423 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2473,12 +2473,12 @@ class ActionDelegate { } /** - * search elastic with string query for cases and default page size of 1000 cases + * search elastic with string query for cases and default page size of 100 cases * @param query string with search conditions * @param pageSize optional parameter which decides number of returned elements - * @return list of cases (default max 1000) which match condition + * @return list of cases (default max 100) which match condition */ - List findCasesElastic(String query, int pageSize = 1000) { + List findCasesElastic(String query, int pageSize = 100) { this.findCasesElastic(query, PageRequest.of(0, pageSize)) } From 721166065c0954b752e6e283e85e16a25e915c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Fri, 14 Feb 2025 15:03:52 +0100 Subject: [PATCH 025/174] [NAE-2039] Search in workflow view - resolve PR comments --- .../service/ElasticPetriNetService.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java index 30d4eb3b056..6c8a7c8a92f 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.elastic.service; import com.netgrif.application.engine.auth.domain.LoggedUser; +import com.netgrif.application.engine.configuration.ElasticsearchConfiguration; import com.netgrif.application.engine.elastic.domain.ElasticPetriNet; import com.netgrif.application.engine.elastic.domain.ElasticPetriNetRepository; @@ -15,7 +16,6 @@ import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Page; @@ -47,13 +47,13 @@ public class ElasticPetriNetService implements IElasticPetriNetService { private final ElasticsearchRestTemplate template; - @Value("${spring.data.elasticsearch.index.petrinet}") - protected String netIndex; + protected ElasticsearchConfiguration elasticsearchConfiguration; - public ElasticPetriNetService(ElasticPetriNetRepository repository, Executor executors, ElasticsearchRestTemplate template) { + public ElasticPetriNetService(ElasticPetriNetRepository repository, Executor executors, ElasticsearchRestTemplate template, ElasticsearchConfiguration elasticsearchConfiguration) { this.repository = repository; this.executors = executors; this.template = template; + this.elasticsearchConfiguration = elasticsearchConfiguration; } @Lazy @@ -116,6 +116,15 @@ public List findAllByUriNodeId(String uriNodeId) { return petriNetService.findAllById(elasticPetriNets.stream().map(ElasticPetriNet::getStringId).collect(Collectors.toList())); } + /** + * Method for search of PetriNets in Elastic + * @param requests - search body, for now only title working + * @param user - logged user + * @param pageable - pageable for paging + * @param locale - internacionalization + * @param isIntersection - property for merging filter, not implemented now, use false + * @return Page - page of PetriNetReferences + */ @Override public Page search(PetriNetSearch requests, LoggedUser user, Pageable pageable, Locale locale, Boolean isIntersection) { if (requests == null) { @@ -127,7 +136,7 @@ public Page search(PetriNetSearch requests, LoggedUser user, List netPage; long total; if (query != null) { - SearchHits hits = template.search(query, ElasticPetriNet.class, IndexCoordinates.of(netIndex)); + SearchHits hits = template.search(query, ElasticPetriNet.class, IndexCoordinates.of(elasticsearchConfiguration.elasticPetriNetIndex())); Page indexedNets = (Page) SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(hits, query.getPageable())); netPage = petriNetService.findAllById(indexedNets.get().map(ElasticPetriNet::getStringId).collect(Collectors.toList())); total = indexedNets.getTotalElements(); @@ -149,7 +158,7 @@ protected NativeSearchQuery buildQuery(PetriNetSearch request, LoggedUser user, return null; } else if (!isIntersection) { singleQueries = singleQueries.stream().filter(Objects::nonNull).collect(Collectors.toList()); - if (singleQueries.size() == 0) { + if (singleQueries.isEmpty()) { // all queries result in an empty set => the entire result is an empty set return null; } @@ -197,7 +206,7 @@ protected boolean buildGroupQuery(PetriNetSearch request, LoggedUser user, Local PetriNetSearch processQuery = new PetriNetSearch(); processQuery.setGroup(request.getGroup()); List groupProcesses = this.petriNetService.search(processQuery, user, new FullPageRequest(), locale).getContent(); - if (groupProcesses.size() == 0) + if (groupProcesses.isEmpty()) return true; BoolQueryBuilder groupQuery = boolQuery(); From fa81b0270b0b43bdf792d4eebe41c5d180f8a0b5 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 14 Feb 2025 16:00:01 +0100 Subject: [PATCH 026/174] [NAE-2052] Integrate ticket view with menu items - add MenuItemView.isPrimary attribute - fix options initializing in configuration --- .../engine/menu/domain/MenuItemView.java | 22 +++++++++++++------ .../services/interfaces/IMenuItemService.java | 5 +++-- .../engine-processes/menu/menu_item.xml | 6 ++--- .../menu/tabbed_case_view_configuration.xml | 2 +- .../menu/tabbed_ticket_view_configuration.xml | 2 +- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 68af7cda5c3..1bdf5a43d54 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -15,14 +15,15 @@ @Getter public enum MenuItemView { TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", - "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), true), + "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), + true, true), TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", - "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true), + "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", - List.of("tabbed_single_task_view"), true), + List.of("tabbed_single_task_view"), true, true), TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true); + "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); private final I18nString name; private final String identifier; @@ -31,12 +32,18 @@ public enum MenuItemView { * */ private final List allowedAssociatedViews; private final boolean isTabbed; + /** + * if false, the view cannot be used as first configuration of the menu_item, but can be used as secondary + * (associated to another view) + * */ + private final boolean isPrimary; - MenuItemView(I18nString name, String identifier, List allowedAssociatedViews, boolean isTabbed) { + MenuItemView(I18nString name, String identifier, List allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { this.name = name; this.identifier = identifier; this.allowedAssociatedViews = allowedAssociatedViews; this.isTabbed = isTabbed; + this.isPrimary = isPrimary; } /** @@ -55,12 +62,13 @@ public static MenuItemView fromIdentifier(String identifier) { * Finds all enum values, that are tabbed or non-tabbed * * @param isTabbed if true, only tabbed values will be returned + * @param isPrimary if true, only views accessible directly from the menu_item will be returned * * @return List of views based on {@link #isTabbed} * */ - public static List findAllByIsTabbed(boolean isTabbed) { + public static List findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed) + .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) .collect(Collectors.toList()); } diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java index 7bdd4c26b78..9de2a7f2308 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java @@ -32,12 +32,13 @@ public interface IMenuItemService { * Gets all tabbed or non-tabbed views * * @param isTabbed if true, only tabbed views will be returned + * @param isPrimary if true, only views accessible directly from the menu_item will be returned * * @return All available views defined in {@link MenuItemView} in consideration of input value. Views are returned as * options for {@link MapOptionsField} * */ - default Map getAvailableViewsAsOptions(boolean isTabbed) { - return MenuItemView.findAllByIsTabbed(isTabbed).stream() + default Map getAvailableViewsAsOptions(boolean isTabbed, boolean isPrimary) { + return MenuItemView.findAllByIsTabbedAndIsPrimary(isTabbed, isPrimary).stream() .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); } diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index f47c948f8d3..327dcc656c6 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -473,14 +473,14 @@ view_configuration_type Pick view type - menuItemService.getAvailableViewsAsOptions(false) + menuItemService.getAvailableViewsAsOptions(false, true) use_tabbed_view: f.use_tabbed_view, view_configuration_type: f.view_configuration_type; if (view_configuration_type.options == null || view_configuration_type.options.isEmpty()) { - change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value) } + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value, true) } } @@ -1021,7 +1021,7 @@ manageBehaviorOfTabFields(use_tab_icon.value, use_tabbed_view.value) change view_configuration_type value { null } - change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value) } + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value, true) } diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 57908c31a62..3e1adad7aa7 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -377,7 +377,7 @@ view_configuration_type: f.view_configuration_type; if (view_configuration_type.options == null || view_configuration_type.options.isEmpty()) { - change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(true) } + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(true, useCase.processIdentifier) } } diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 7608ab2b274..b68407bc78a 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -204,7 +204,7 @@ view_configuration_type: f.view_configuration_type; if (view_configuration_type.options == null || view_configuration_type.options.isEmpty()) { - change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(true) } + change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(true, useCase.processIdentifier) } } From c550d27fa2d3bd0e73e693f182bc474367e22c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Mon, 17 Feb 2025 09:52:35 +0100 Subject: [PATCH 027/174] [NAE-2054] Release 6.5.0 CE - fix after merge --- .../resources/petriNets/engine-processes/dashboard_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index d73b04e8dc6..33f2ce15d4c 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -67,7 +67,7 @@ preference_items_list: f.preference_items_list; - def preferenceItemCases = findCasesElastic("processIdentifier:\"preference_item\"" as String) + def preferenceItemCases = findCasesElastic("processIdentifier:\"menu_item\"" as String) .collectEntries { [(it.stringId): it.dataSet["menu_name"].value] } change preference_items_list options { preferenceItemCases } From 31e21f4983a98e6bff99bc1fafebc7b15b429c05 Mon Sep 17 00:00:00 2001 From: Machac Date: Mon, 17 Feb 2025 10:34:05 +0100 Subject: [PATCH 028/174] Release/6.2.10 --- CHANGELOG.md | 9 ++++++++- docker-compose.yml | 2 +- pom.xml | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56a4d2031df..8afbe5f6c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -Full Changelog: [https://github.com/netgrif/application-engine/commits/v6.2.9](https://github.com/netgrif/application-engine/commits/v6.2.9) +Full Changelog: [https://github.com/netgrif/application-engine/commits/v6.2.10](https://github.com/netgrif/application-engine/commits/v6.2.10) + +## [6.2.10](https://github.com/netgrif/application-engine/releases/tag/v6.2.9) (2024-02-17) + +### Fixed + +- [NAE-1921] User field value cannot be cleared +- [NAE-2053] Optimize ElasticCaseService queries to eliminate maxClauseCount error ## [6.2.9](https://github.com/netgrif/application-engine/releases/tag/v6.2.9) (2023-05-04) diff --git a/docker-compose.yml b/docker-compose.yml index 8c0780e3771..6ca387520cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: memory: "512M" docker-elastic: - image: elasticsearch:7.17.4 + image: elasticsearch:7.17.26 environment: - cluster.name=elasticsearch - discovery.type=single-node diff --git a/pom.xml b/pom.xml index 98e0fe201c4..9750dcad809 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.netgrif application-engine - 6.2.9-SNAPSHOT + 6.2.10 jar NETGRIF Application Engine From 32c8a4b64dc609fd835dd31ea21755a8e4444410 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 17 Feb 2025 11:15:27 +0100 Subject: [PATCH 029/174] [NAE-2055] Rework enum registry to service registry - implement MenuItemViewRegistry --- .../logic/action/ActionDelegate.groovy | 1 - .../engine/startup/FilterRunner.groovy | 4 +- .../engine/menu/domain/MenuItemView.java | 81 +++-------------- .../engine/menu/domain/MenuItemViewOLD.java | 90 +++++++++++++++++++ .../configurations/TabbedCaseViewBody.java | 6 +- .../TabbedSingleTaskViewBody.java | 6 +- .../configurations/TabbedTaskViewBody.java | 6 +- .../configurations/TabbedTicketViewBody.java | 6 +- .../menu/domain/configurations/ViewBody.java | 4 +- .../menu/registry/MenuItemViewRegistry.java | 73 +++++++++++++++ .../interfaces/IMenuItemViewRegistry.java | 16 ++++ .../services/interfaces/IMenuItemService.java | 16 ++-- .../engine/action/MenuItemApiTest.groovy | 10 +-- 13 files changed, 218 insertions(+), 101 deletions(-) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 68219a040fa..7edc40094ac 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -26,7 +26,6 @@ import com.netgrif.application.engine.mail.interfaces.IMailService import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants -import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index c318595f541..902d88a4444 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -1,6 +1,6 @@ package com.netgrif.application.engine.startup -import com.netgrif.application.engine.menu.domain.MenuItemView +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD import com.netgrif.application.engine.petrinet.domain.PetriNet import com.netgrif.application.engine.petrinet.domain.VersionType import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService @@ -51,7 +51,7 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { } List createConfigurationNets() { - return MenuItemView.values().each { view -> + return MenuItemViewOLD.values().each { view -> String processIdentifier = view.getIdentifier() + "_configuration" String filePath = String.format("engine-processes/menu/%s.xml", processIdentifier) importProcess(String.format("Petri net for %s", processIdentifier), processIdentifier, filePath) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 1bdf5a43d54..65c9f10b7c4 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -1,31 +1,20 @@ package com.netgrif.application.engine.menu.domain; import com.netgrif.application.engine.petrinet.domain.I18nString; -import lombok.Getter; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; -import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - - -/** - * Here is listed and configured every configuration process available for menu items. - * */ -@Getter -public enum MenuItemView { - TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", - "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), - true, true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", - "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), - TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", - "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", - List.of("tabbed_single_task_view"), true, true), - TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); +@Data +@Builder +@AllArgsConstructor +public class MenuItemView { + @NonNull private final I18nString name; + @NonNull private final String identifier; /** * List of view identifiers of views, that can be associated with the view @@ -37,54 +26,4 @@ public enum MenuItemView { * (associated to another view) * */ private final boolean isPrimary; - - MenuItemView(I18nString name, String identifier, List allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { - this.name = name; - this.identifier = identifier; - this.allowedAssociatedViews = allowedAssociatedViews; - this.isTabbed = isTabbed; - this.isPrimary = isPrimary; - } - - /** - * Builds enum value by the view identifier - * */ - public static MenuItemView fromIdentifier(String identifier) { - for (MenuItemView view : MenuItemView.values()) { - if (view.identifier.equals(identifier)) { - return view; - } - } - throw new IllegalArgumentException(identifier); - } - - /** - * Finds all enum values, that are tabbed or non-tabbed - * - * @param isTabbed if true, only tabbed values will be returned - * @param isPrimary if true, only views accessible directly from the menu_item will be returned - * - * @return List of views based on {@link #isTabbed} - * */ - public static List findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { - return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) - .collect(Collectors.toList()); - } - - /** - * Finds all enum values, that are tabbed or non-tabbed and are defined in parent view as {@link #allowedAssociatedViews} - * - * @param isTabbed if true, set of views is reduced to only tabbed views - * @param parentIdentifier identifier of the view, that contains returned views in {@link #allowedAssociatedViews} - * - * @return List of views based on {@link #isTabbed} and {@link #allowedAssociatedViews} - * */ - public static List findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { - MenuItemView parentView = fromIdentifier(parentIdentifier); - return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed - && parentView.getAllowedAssociatedViews().contains(view.identifier)) - .collect(Collectors.toList()); - } } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java new file mode 100644 index 00000000000..4e806ad576d --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java @@ -0,0 +1,90 @@ +package com.netgrif.application.engine.menu.domain; + +import com.netgrif.application.engine.petrinet.domain.I18nString; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * Here is listed and configured every configuration process available for menu items. + * */ +@Getter +public enum MenuItemViewOLD { + TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", + "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), + true, true), + TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", + "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), + TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", + "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", + List.of("tabbed_single_task_view"), true, true), + TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", + "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); + + private final I18nString name; + private final String identifier; + /** + * List of view identifiers of views, that can be associated with the view + * */ + private final List allowedAssociatedViews; + private final boolean isTabbed; + /** + * if false, the view cannot be used as first configuration of the menu_item, but can be used as secondary + * (associated to another view) + * */ + private final boolean isPrimary; + + MenuItemViewOLD(I18nString name, String identifier, List allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { + this.name = name; + this.identifier = identifier; + this.allowedAssociatedViews = allowedAssociatedViews; + this.isTabbed = isTabbed; + this.isPrimary = isPrimary; + } + + /** + * Builds enum value by the view identifier + * */ + public static MenuItemViewOLD fromIdentifier(String identifier) { + for (MenuItemViewOLD view : MenuItemViewOLD.values()) { + if (view.identifier.equals(identifier)) { + return view; + } + } + throw new IllegalArgumentException(identifier); + } + + /** + * Finds all enum values, that are tabbed or non-tabbed + * + * @param isTabbed if true, only tabbed values will be returned + * @param isPrimary if true, only views accessible directly from the menu_item will be returned + * + * @return List of views based on {@link #isTabbed} + * */ + public static List findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { + return Arrays.stream(MenuItemViewOLD.values()) + .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) + .collect(Collectors.toList()); + } + + /** + * Finds all enum values, that are tabbed or non-tabbed and are defined in parent view as {@link #allowedAssociatedViews} + * + * @param isTabbed if true, set of views is reduced to only tabbed views + * @param parentIdentifier identifier of the view, that contains returned views in {@link #allowedAssociatedViews} + * + * @return List of views based on {@link #isTabbed} and {@link #allowedAssociatedViews} + * */ + public static List findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { + MenuItemViewOLD parentView = fromIdentifier(parentIdentifier); + return Arrays.stream(MenuItemViewOLD.values()) + .filter(view -> view.isTabbed == isTabbed + && parentView.getAllowedAssociatedViews().contains(view.identifier)) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index 5f18b2920cb..71e1bfcf475 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import lombok.Data; @@ -36,8 +36,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemView getViewType() { - return MenuItemView.TABBED_CASE_VIEW; + public MenuItemViewOLD getViewType() { + return MenuItemViewOLD.TABBED_CASE_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java index fa58689c976..76020fbddd3 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import lombok.Data; @@ -19,8 +19,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemView getViewType() { - return MenuItemView.TABBED_SINGLE_TASK_VIEW; + public MenuItemViewOLD getViewType() { + return MenuItemViewOLD.TABBED_SINGLE_TASK_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java index 0fabe6940fe..ab95e57a1f2 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.workflow.domain.Case; @@ -32,8 +32,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemView getViewType() { - return MenuItemView.TABBED_TASK_VIEW; + public MenuItemViewOLD getViewType() { + return MenuItemViewOLD.TABBED_TASK_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java index f0aafd54b65..1964f543c60 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import lombok.Data; import lombok.EqualsAndHashCode; @@ -19,8 +19,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemView getViewType() { - return MenuItemView.TABBED_TICKET_VIEW; + public MenuItemViewOLD getViewType() { + return MenuItemViewOLD.TABBED_TICKET_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java index d636b6e60b1..bbde55d64ff 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -1,7 +1,7 @@ package com.netgrif.application.engine.menu.domain.configurations; import com.netgrif.application.engine.menu.domain.FilterBody; -import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; @@ -23,7 +23,7 @@ public abstract class ViewBody { protected FilterBody filterBody; public abstract ViewBody getAssociatedViewBody(); - public abstract MenuItemView getViewType(); + public abstract MenuItemViewOLD getViewType(); /** * Internal method, that must transform data in concrete class and add them into received outcome. Method must return * the updated outcome. diff --git a/src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java b/src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java new file mode 100644 index 00000000000..030a17a8ce3 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java @@ -0,0 +1,73 @@ +package com.netgrif.application.engine.menu.registry; + +import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.registry.interfaces.IMenuItemViewRegistry; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class MenuItemViewRegistry implements IMenuItemViewRegistry { + + /** + * todo javadoc + * */ + private final Map views; + + public MenuItemViewRegistry() { + this.views = new ConcurrentHashMap<>(); + } + + /** + * todo javadoc + * */ + @Override + public void registerView(@Validated MenuItemView view) { + this.views.put(view.getIdentifier(), view); + log.debug("Registered menu item view [{}] with identifier [{}]", view.getName().getDefaultValue(), view.getIdentifier()); + } + + /** + * todo javadoc + * */ + @Override + public MenuItemView getViewByIdentifier(String identifier) { + return this.views.get(identifier); + } + + /** + * todo javadoc + * */ + @Override + public Map getAllViews() { + return this.views; + } + + /** + * todo javadoc + * */ + @Override + public List getAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { + return this.views.values().stream() + .filter(menuItemView -> menuItemView.isTabbed() == isTabbed && menuItemView.isPrimary() == isPrimary) + .collect(Collectors.toList()); + } + + /** + * todo javadoc + * */ + @Override + public List getAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { + MenuItemView parentView = getViewByIdentifier(parentIdentifier); + return this.views.values().stream() + .filter(menuItemView -> menuItemView.isTabbed() == isTabbed + && parentView.getAllowedAssociatedViews().contains(menuItemView.getIdentifier())) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java b/src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java new file mode 100644 index 00000000000..35de383918c --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java @@ -0,0 +1,16 @@ +package com.netgrif.application.engine.menu.registry.interfaces; + +import com.netgrif.application.engine.menu.domain.MenuItemView; + +import java.util.Map; +import java.util.List; + +public interface IMenuItemViewRegistry { + + void registerView(MenuItemView view); + MenuItemView getViewByIdentifier(String identifier); + Map getAllViews(); + List getAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary); + List getAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier); + +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java index 9de2a7f2308..7ea9aaef448 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java @@ -2,7 +2,7 @@ import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; -import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriNode; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; @@ -34,21 +34,21 @@ public interface IMenuItemService { * @param isTabbed if true, only tabbed views will be returned * @param isPrimary if true, only views accessible directly from the menu_item will be returned * - * @return All available views defined in {@link MenuItemView} in consideration of input value. Views are returned as + * @return All available views defined in {@link MenuItemViewOLD} in consideration of input value. Views are returned as * options for {@link MapOptionsField} * */ default Map getAvailableViewsAsOptions(boolean isTabbed, boolean isPrimary) { - return MenuItemView.findAllByIsTabbedAndIsPrimary(isTabbed, isPrimary).stream() - .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); + return MenuItemViewOLD.findAllByIsTabbedAndIsPrimary(isTabbed, isPrimary).stream() + .collect(Collectors.toMap(MenuItemViewOLD::getIdentifier, MenuItemViewOLD::getName)); } /** * Gets all tabbed or non-tabbed views * * @param isTabbed if true, only tabbed views will be returned - * @param viewIdentifier identifier of view (defined in {@link MenuItemView}), which is parent to returned views + * @param viewIdentifier identifier of view (defined in {@link MenuItemViewOLD}), which is parent to returned views * - * @return All available views defined in {@link MenuItemView} in consideration of input values. Views are returned as + * @return All available views defined in {@link MenuItemViewOLD} in consideration of input values. Views are returned as * options for {@link MapOptionsField} * */ default Map getAvailableViewsAsOptions(boolean isTabbed, String viewIdentifier) { @@ -56,8 +56,8 @@ default Map getAvailableViewsAsOptions(boolean isTabbed, Str if (index > 0) { viewIdentifier = viewIdentifier.substring(0, index); } - return MenuItemView.findAllByIsTabbedAndParentIdentifier(isTabbed, viewIdentifier).stream() - .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); + return MenuItemViewOLD.findAllByIsTabbedAndParentIdentifier(isTabbed, viewIdentifier).stream() + .collect(Collectors.toMap(MenuItemViewOLD::getIdentifier, MenuItemViewOLD::getName)); } } diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index e3ae062e5e0..69103af53bb 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -5,7 +5,7 @@ import com.netgrif.application.engine.auth.service.interfaces.IUserService import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest import com.netgrif.application.engine.menu.domain.MenuItemConstants -import com.netgrif.application.engine.menu.domain.MenuItemView +import com.netgrif.application.engine.menu.domain.MenuItemViewOLD import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewConstants import com.netgrif.application.engine.menu.utils.MenuItemUtils @@ -91,7 +91,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_BANNED_ROLES].options.containsKey("role_2:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.containsKey("role_1:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_CASE_VIEW.identifier assert filter.dataSet["filter"].filterMetadata["filterType"] == "Case" assert filter.dataSet["filter"].allowedNets == ["filter", "menu_item"] @@ -104,7 +104,7 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null @@ -157,7 +157,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_MENU_NAME].value.toString() == "CHANGED FILTER" assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.entrySet()[0].key.contains("role_2") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_CASE_VIEW.identifier assert item.uriNodeId == newUri.stringId assert filter.dataSet["filter"].allowedNets == ["filter"] @@ -170,7 +170,7 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) From 56d016a74b43a6339826e8492ef6360859eba18c Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 17 Feb 2025 11:17:36 +0100 Subject: [PATCH 030/174] Revert "[NAE-2055] Rework enum registry to service registry" This reverts commit 32c8a4b64dc609fd835dd31ea21755a8e4444410. --- .../logic/action/ActionDelegate.groovy | 1 + .../engine/startup/FilterRunner.groovy | 4 +- .../engine/menu/domain/MenuItemView.java | 81 ++++++++++++++--- .../engine/menu/domain/MenuItemViewOLD.java | 90 ------------------- .../configurations/TabbedCaseViewBody.java | 6 +- .../TabbedSingleTaskViewBody.java | 6 +- .../configurations/TabbedTaskViewBody.java | 6 +- .../configurations/TabbedTicketViewBody.java | 6 +- .../menu/domain/configurations/ViewBody.java | 4 +- .../menu/registry/MenuItemViewRegistry.java | 73 --------------- .../interfaces/IMenuItemViewRegistry.java | 16 ---- .../services/interfaces/IMenuItemService.java | 16 ++-- .../engine/action/MenuItemApiTest.groovy | 10 +-- 13 files changed, 101 insertions(+), 218 deletions(-) delete mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java delete mode 100644 src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java delete mode 100644 src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 7edc40094ac..68219a040fa 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -26,6 +26,7 @@ import com.netgrif.application.engine.mail.interfaces.IMailService import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants +import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index 902d88a4444..c318595f541 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -1,6 +1,6 @@ package com.netgrif.application.engine.startup -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD +import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.petrinet.domain.PetriNet import com.netgrif.application.engine.petrinet.domain.VersionType import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService @@ -51,7 +51,7 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { } List createConfigurationNets() { - return MenuItemViewOLD.values().each { view -> + return MenuItemView.values().each { view -> String processIdentifier = view.getIdentifier() + "_configuration" String filePath = String.format("engine-processes/menu/%s.xml", processIdentifier) importProcess(String.format("Petri net for %s", processIdentifier), processIdentifier, filePath) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 65c9f10b7c4..1bdf5a43d54 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -1,20 +1,31 @@ package com.netgrif.application.engine.menu.domain; import com.netgrif.application.engine.petrinet.domain.I18nString; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NonNull; +import lombok.Getter; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * Here is listed and configured every configuration process available for menu items. + * */ +@Getter +public enum MenuItemView { + TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", + "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), + true, true), + TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", + "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), + TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", + "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", + List.of("tabbed_single_task_view"), true, true), + TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", + "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); -@Data -@Builder -@AllArgsConstructor -public class MenuItemView { - @NonNull private final I18nString name; - @NonNull private final String identifier; /** * List of view identifiers of views, that can be associated with the view @@ -26,4 +37,54 @@ public class MenuItemView { * (associated to another view) * */ private final boolean isPrimary; + + MenuItemView(I18nString name, String identifier, List allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { + this.name = name; + this.identifier = identifier; + this.allowedAssociatedViews = allowedAssociatedViews; + this.isTabbed = isTabbed; + this.isPrimary = isPrimary; + } + + /** + * Builds enum value by the view identifier + * */ + public static MenuItemView fromIdentifier(String identifier) { + for (MenuItemView view : MenuItemView.values()) { + if (view.identifier.equals(identifier)) { + return view; + } + } + throw new IllegalArgumentException(identifier); + } + + /** + * Finds all enum values, that are tabbed or non-tabbed + * + * @param isTabbed if true, only tabbed values will be returned + * @param isPrimary if true, only views accessible directly from the menu_item will be returned + * + * @return List of views based on {@link #isTabbed} + * */ + public static List findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { + return Arrays.stream(MenuItemView.values()) + .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) + .collect(Collectors.toList()); + } + + /** + * Finds all enum values, that are tabbed or non-tabbed and are defined in parent view as {@link #allowedAssociatedViews} + * + * @param isTabbed if true, set of views is reduced to only tabbed views + * @param parentIdentifier identifier of the view, that contains returned views in {@link #allowedAssociatedViews} + * + * @return List of views based on {@link #isTabbed} and {@link #allowedAssociatedViews} + * */ + public static List findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { + MenuItemView parentView = fromIdentifier(parentIdentifier); + return Arrays.stream(MenuItemView.values()) + .filter(view -> view.isTabbed == isTabbed + && parentView.getAllowedAssociatedViews().contains(view.identifier)) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java deleted file mode 100644 index 4e806ad576d..00000000000 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewOLD.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.netgrif.application.engine.menu.domain; - -import com.netgrif.application.engine.petrinet.domain.I18nString; -import lombok.Getter; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - - -/** - * Here is listed and configured every configuration process available for menu items. - * */ -@Getter -public enum MenuItemViewOLD { - TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", - "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), - true, true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", - "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), - TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", - "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", - List.of("tabbed_single_task_view"), true, true), - TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); - - private final I18nString name; - private final String identifier; - /** - * List of view identifiers of views, that can be associated with the view - * */ - private final List allowedAssociatedViews; - private final boolean isTabbed; - /** - * if false, the view cannot be used as first configuration of the menu_item, but can be used as secondary - * (associated to another view) - * */ - private final boolean isPrimary; - - MenuItemViewOLD(I18nString name, String identifier, List allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { - this.name = name; - this.identifier = identifier; - this.allowedAssociatedViews = allowedAssociatedViews; - this.isTabbed = isTabbed; - this.isPrimary = isPrimary; - } - - /** - * Builds enum value by the view identifier - * */ - public static MenuItemViewOLD fromIdentifier(String identifier) { - for (MenuItemViewOLD view : MenuItemViewOLD.values()) { - if (view.identifier.equals(identifier)) { - return view; - } - } - throw new IllegalArgumentException(identifier); - } - - /** - * Finds all enum values, that are tabbed or non-tabbed - * - * @param isTabbed if true, only tabbed values will be returned - * @param isPrimary if true, only views accessible directly from the menu_item will be returned - * - * @return List of views based on {@link #isTabbed} - * */ - public static List findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { - return Arrays.stream(MenuItemViewOLD.values()) - .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) - .collect(Collectors.toList()); - } - - /** - * Finds all enum values, that are tabbed or non-tabbed and are defined in parent view as {@link #allowedAssociatedViews} - * - * @param isTabbed if true, set of views is reduced to only tabbed views - * @param parentIdentifier identifier of the view, that contains returned views in {@link #allowedAssociatedViews} - * - * @return List of views based on {@link #isTabbed} and {@link #allowedAssociatedViews} - * */ - public static List findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { - MenuItemViewOLD parentView = fromIdentifier(parentIdentifier); - return Arrays.stream(MenuItemViewOLD.values()) - .filter(view -> view.isTabbed == isTabbed - && parentView.getAllowedAssociatedViews().contains(view.identifier)) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index 71e1bfcf475..5f18b2920cb 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; +import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import lombok.Data; @@ -36,8 +36,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemViewOLD getViewType() { - return MenuItemViewOLD.TABBED_CASE_VIEW; + public MenuItemView getViewType() { + return MenuItemView.TABBED_CASE_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java index 76020fbddd3..fa58689c976 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; +import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import lombok.Data; @@ -19,8 +19,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemViewOLD getViewType() { - return MenuItemViewOLD.TABBED_SINGLE_TASK_VIEW; + public MenuItemView getViewType() { + return MenuItemView.TABBED_SINGLE_TASK_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java index ab95e57a1f2..0fabe6940fe 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; +import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.workflow.domain.Case; @@ -32,8 +32,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemViewOLD getViewType() { - return MenuItemViewOLD.TABBED_TASK_VIEW; + public MenuItemView getViewType() { + return MenuItemView.TABBED_TASK_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java index 1964f543c60..f0aafd54b65 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java @@ -1,6 +1,6 @@ package com.netgrif.application.engine.menu.domain.configurations; -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; +import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import lombok.Data; import lombok.EqualsAndHashCode; @@ -19,8 +19,8 @@ public ViewBody getAssociatedViewBody() { } @Override - public MenuItemViewOLD getViewType() { - return MenuItemViewOLD.TABBED_TICKET_VIEW; + public MenuItemView getViewType() { + return MenuItemView.TABBED_TICKET_VIEW; } @Override diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java index bbde55d64ff..d636b6e60b1 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -1,7 +1,7 @@ package com.netgrif.application.engine.menu.domain.configurations; import com.netgrif.application.engine.menu.domain.FilterBody; -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; +import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; @@ -23,7 +23,7 @@ public abstract class ViewBody { protected FilterBody filterBody; public abstract ViewBody getAssociatedViewBody(); - public abstract MenuItemViewOLD getViewType(); + public abstract MenuItemView getViewType(); /** * Internal method, that must transform data in concrete class and add them into received outcome. Method must return * the updated outcome. diff --git a/src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java b/src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java deleted file mode 100644 index 030a17a8ce3..00000000000 --- a/src/main/java/com/netgrif/application/engine/menu/registry/MenuItemViewRegistry.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.netgrif.application.engine.menu.registry; - -import com.netgrif.application.engine.menu.domain.MenuItemView; -import com.netgrif.application.engine.menu.registry.interfaces.IMenuItemViewRegistry; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.validation.annotation.Validated; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -@Slf4j -@Component -public class MenuItemViewRegistry implements IMenuItemViewRegistry { - - /** - * todo javadoc - * */ - private final Map views; - - public MenuItemViewRegistry() { - this.views = new ConcurrentHashMap<>(); - } - - /** - * todo javadoc - * */ - @Override - public void registerView(@Validated MenuItemView view) { - this.views.put(view.getIdentifier(), view); - log.debug("Registered menu item view [{}] with identifier [{}]", view.getName().getDefaultValue(), view.getIdentifier()); - } - - /** - * todo javadoc - * */ - @Override - public MenuItemView getViewByIdentifier(String identifier) { - return this.views.get(identifier); - } - - /** - * todo javadoc - * */ - @Override - public Map getAllViews() { - return this.views; - } - - /** - * todo javadoc - * */ - @Override - public List getAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { - return this.views.values().stream() - .filter(menuItemView -> menuItemView.isTabbed() == isTabbed && menuItemView.isPrimary() == isPrimary) - .collect(Collectors.toList()); - } - - /** - * todo javadoc - * */ - @Override - public List getAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { - MenuItemView parentView = getViewByIdentifier(parentIdentifier); - return this.views.values().stream() - .filter(menuItemView -> menuItemView.isTabbed() == isTabbed - && parentView.getAllowedAssociatedViews().contains(menuItemView.getIdentifier())) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java b/src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java deleted file mode 100644 index 35de383918c..00000000000 --- a/src/main/java/com/netgrif/application/engine/menu/registry/interfaces/IMenuItemViewRegistry.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.netgrif.application.engine.menu.registry.interfaces; - -import com.netgrif.application.engine.menu.domain.MenuItemView; - -import java.util.Map; -import java.util.List; - -public interface IMenuItemViewRegistry { - - void registerView(MenuItemView view); - MenuItemView getViewByIdentifier(String identifier); - Map getAllViews(); - List getAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary); - List getAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier); - -} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java index 7ea9aaef448..9de2a7f2308 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java @@ -2,7 +2,7 @@ import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD; +import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriNode; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; @@ -34,21 +34,21 @@ public interface IMenuItemService { * @param isTabbed if true, only tabbed views will be returned * @param isPrimary if true, only views accessible directly from the menu_item will be returned * - * @return All available views defined in {@link MenuItemViewOLD} in consideration of input value. Views are returned as + * @return All available views defined in {@link MenuItemView} in consideration of input value. Views are returned as * options for {@link MapOptionsField} * */ default Map getAvailableViewsAsOptions(boolean isTabbed, boolean isPrimary) { - return MenuItemViewOLD.findAllByIsTabbedAndIsPrimary(isTabbed, isPrimary).stream() - .collect(Collectors.toMap(MenuItemViewOLD::getIdentifier, MenuItemViewOLD::getName)); + return MenuItemView.findAllByIsTabbedAndIsPrimary(isTabbed, isPrimary).stream() + .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); } /** * Gets all tabbed or non-tabbed views * * @param isTabbed if true, only tabbed views will be returned - * @param viewIdentifier identifier of view (defined in {@link MenuItemViewOLD}), which is parent to returned views + * @param viewIdentifier identifier of view (defined in {@link MenuItemView}), which is parent to returned views * - * @return All available views defined in {@link MenuItemViewOLD} in consideration of input values. Views are returned as + * @return All available views defined in {@link MenuItemView} in consideration of input values. Views are returned as * options for {@link MapOptionsField} * */ default Map getAvailableViewsAsOptions(boolean isTabbed, String viewIdentifier) { @@ -56,8 +56,8 @@ default Map getAvailableViewsAsOptions(boolean isTabbed, Str if (index > 0) { viewIdentifier = viewIdentifier.substring(0, index); } - return MenuItemViewOLD.findAllByIsTabbedAndParentIdentifier(isTabbed, viewIdentifier).stream() - .collect(Collectors.toMap(MenuItemViewOLD::getIdentifier, MenuItemViewOLD::getName)); + return MenuItemView.findAllByIsTabbedAndParentIdentifier(isTabbed, viewIdentifier).stream() + .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName)); } } diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index 69103af53bb..e3ae062e5e0 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -5,7 +5,7 @@ import com.netgrif.application.engine.auth.service.interfaces.IUserService import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest import com.netgrif.application.engine.menu.domain.MenuItemConstants -import com.netgrif.application.engine.menu.domain.MenuItemViewOLD +import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewConstants import com.netgrif.application.engine.menu.utils.MenuItemUtils @@ -91,7 +91,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_BANNED_ROLES].options.containsKey("role_2:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.containsKey("role_1:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier assert filter.dataSet["filter"].filterMetadata["filterType"] == "Case" assert filter.dataSet["filter"].allowedNets == ["filter", "menu_item"] @@ -104,7 +104,7 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null @@ -157,7 +157,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_MENU_NAME].value.toString() == "CHANGED FILTER" assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.entrySet()[0].key.contains("role_2") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier assert item.uriNodeId == newUri.stringId assert filter.dataSet["filter"].allowedNets == ["filter"] @@ -170,7 +170,7 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemViewOLD.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) From afe468f193568846ce4f91a3e7ec4175b8e94c8e Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 17 Feb 2025 11:21:27 +0100 Subject: [PATCH 031/174] [NAE-2051] Implement configurable view in menu items - add map initialization in MenuItemBody --- .../netgrif/application/engine/menu/domain/MenuItemBody.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 8440705594a..1d644b644be 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -10,6 +10,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,8 +25,8 @@ public class MenuItemBody { private String menuIcon = "filter_none"; private I18nString menuName; - private Map allowedRoles; - private Map bannedRoles; + private Map allowedRoles = new HashMap<>(); + private Map bannedRoles = new HashMap<>(); private boolean useCustomView = false; private String customViewSelector; private boolean isAutoSelect = false; From 67b38ea98a84f766ff75e91e593277dec3205be7 Mon Sep 17 00:00:00 2001 From: Machac Date: Mon, 17 Feb 2025 11:50:12 +0100 Subject: [PATCH 032/174] [NAE-2053] Optimize ElasticCaseService queries to eliminate maxClauseCount error - ElasticCaseService: incorporation of reminders from PR --- .../elastic/service/ElasticCaseService.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java index e8dcfe1ebd5..aeb7f34ffa6 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java @@ -219,14 +219,17 @@ private void buildPetriNetQuery(CaseSearchRequest request, LoggedUser user, Bool return; } - List identifiers = request.process.stream() - .filter(p -> p.identifier != null) - .map(p -> p.identifier) - .collect(Collectors.toList()); - List processIds = request.process.stream() - .filter(p -> p.processId != null) - .map(p -> p.processId) - .collect(Collectors.toList()); + Set identifiers = new HashSet<>(); + Set processIds = new HashSet<>(); + + request.process.forEach(p -> { + if (p.identifier != null) { + identifiers.add(p.identifier); + } + if (p.processId != null) { + processIds.add(p.processId); + } + }); BoolQueryBuilder petriNetQuery = boolQuery(); if (!identifiers.isEmpty()) { @@ -450,8 +453,9 @@ private boolean buildGroupQuery(CaseSearchRequest request, LoggedUser user, Loca Map processQuery = new HashMap<>(); processQuery.put("group", request.group); List groupProcesses = this.petriNetService.search(processQuery, user, new FullPageRequest(), locale).getContent(); - if (groupProcesses.isEmpty()) + if (groupProcesses.isEmpty()) { return true; + } List identifiers = groupProcesses.stream() .map(PetriNetReference::getIdentifier) .collect(Collectors.toList()); From 32f4c1a948581998225c075d8a8ddc970c4e18bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kov=C3=A1=C4=8Dik?= Date: Mon, 17 Feb 2025 11:54:44 +0100 Subject: [PATCH 033/174] [NAE-2054] Release 6.5.0 CE - fix i18nfield in elasticPetriNet index --- .../application/engine/startup/FilterRunner.groovy | 6 ++++++ .../engine/elastic/domain/ElasticPetriNet.java | 14 ++++++++++++-- .../elastic/service/ElasticPetriNetService.java | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index e3d563698f1..d7b12430e14 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -15,6 +15,12 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { @Autowired private ImportHelper helper + @Autowired + private IPetriNetService petriNetService + + @Autowired + private SystemUserRunner systemCreator + private static final String FILTER_FILE_NAME = "engine-processes/filter.xml" public static final String FILTER_PETRI_NET_IDENTIFIER = "filter" diff --git a/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNet.java b/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNet.java index 48637e4c236..537dc92f369 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNet.java +++ b/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNet.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.netgrif.application.engine.elastic.domain.I18nField; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.PetriNet; import com.netgrif.application.engine.petrinet.domain.version.Version; @@ -17,6 +18,8 @@ import org.springframework.data.elasticsearch.annotations.FieldType; import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Set; import static org.springframework.data.elasticsearch.annotations.FieldType.Keyword; @@ -40,7 +43,7 @@ public class ElasticPetriNet { @Field(type = Keyword) private String stringId; - private I18nString title; + private I18nField title; @Field(type = Keyword) private String initials; @@ -55,7 +58,7 @@ public ElasticPetriNet(PetriNet net) { this.version = net.getVersion(); this.uriNodeId = net.getUriNodeId(); this.stringId = net.getStringId(); - this.title = net.getTitle(); + this.title = this.transformToField(net.getTitle()); this.initials = net.getInitials(); this.creationDate = net.getCreationDate(); } @@ -68,4 +71,11 @@ public void update(ElasticPetriNet net) { this.title = net.getTitle(); this.initials = net.getInitials(); } + + protected I18nField transformToField(I18nString field) { + Set keys = field.getTranslations().keySet(); + Set values = new HashSet<>(field.getTranslations().values()); + values.add(field.getDefaultValue()); + return new I18nField(keys, values); + } } diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java index 6c8a7c8a92f..dbaf4ee85c4 100644 --- a/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java +++ b/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java @@ -192,7 +192,7 @@ protected void buildFullTextQuery(PetriNetSearch request, BoolQueryBuilder query String searchText = "*" + request.getTitle() + "*"; Map fields = new HashMap<>(); - fields.put("title.defaultValue", 2f); + fields.put("title.textValue", 2f); fields.put("identifier", 1f); QueryBuilder fullTextQuery = queryStringQuery(searchText).fields(fields); query.must(fullTextQuery); From 1fa70e9449fd1a4a322f11ea395102994bab762e Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 17 Feb 2025 13:15:37 +0100 Subject: [PATCH 034/174] [NAE-2054] Release 6.5.0 CE - update FilterRunner after merge --- .../engine/startup/FilterRunner.groovy | 45 +++---------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index d7b12430e14..bbdb4ceb1c5 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -2,7 +2,6 @@ package com.netgrif.application.engine.startup import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.petrinet.domain.PetriNet -import com.netgrif.application.engine.petrinet.domain.VersionType import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired @@ -35,50 +34,18 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { @Override void run(String... args) throws Exception { - createFilterNet() + helper.importProcess("Petri net for filters", FILTER_PETRI_NET_IDENTIFIER, FILTER_FILE_NAME) createConfigurationNets() - createMenuItemNet() - createImportFiltersNet() - createExportFiltersNet() + helper.importProcess("Petri net for filter preferences", MENU_NET_IDENTIFIER, MENU_ITEM_FILE_NAME) + helper.importProcess("Petri net for importing filters", IMPORT_NET_IDENTIFIER, IMPORT_FILTER_FILE_NAME) + helper.importProcess("Petri net for exporting filters", EXPORT_NET_IDENTIFIER, EXPORT_FILTER_FILE_NAME) } - Optional createFilterNet() { - importProcess("Petri net for filters", FILTER_PETRI_NET_IDENTIFIER, FILTER_FILE_NAME) - } - - Optional createMenuItemNet() { - importProcess("Petri net for filter preferences", MENU_NET_IDENTIFIER, MENU_ITEM_FILE_NAME) - } - - List createConfigurationNets() { + private List createConfigurationNets() { return MenuItemView.values().each { view -> String processIdentifier = view.getIdentifier() + "_configuration" String filePath = String.format("engine-processes/menu/%s.xml", processIdentifier) - importProcess(String.format("Petri net for %s", processIdentifier), processIdentifier, filePath) + helper.importProcess(String.format("Petri net for %s", processIdentifier), processIdentifier, filePath) }.collect() } - - Optional createImportFiltersNet() { - importProcess("Petri net for importing filters", IMPORT_NET_IDENTIFIER, IMPORT_FILTER_FILE_NAME) - } - - Optional createExportFiltersNet() { - importProcess("Petri net for exporting filters", EXPORT_NET_IDENTIFIER, EXPORT_FILTER_FILE_NAME) - } - - Optional importProcess(String message, String netIdentifier, String netFileName) { - PetriNet filter = petriNetService.getNewestVersionByIdentifier(netIdentifier) - if (filter != null) { - log.info("${message} has already been imported.") - return Optional.of(filter) - } - - Optional filterNet = helper.createNet(netFileName, VersionType.MAJOR, systemCreator.loggedSystem) - - if (!filterNet.isPresent()) { - log.error("Import of ${message} failed!") - } - - return filterNet - } } From 282497ad90d781f5d4f639ffb49ec22d79223358 Mon Sep 17 00:00:00 2001 From: Jozef Daxner Date: Tue, 18 Feb 2025 02:11:27 +0100 Subject: [PATCH 035/174] =?UTF-8?q?[NAE-2033]=20=C3=9Avodn=C3=BD=20dashboa?= =?UTF-8?q?rd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rename preference item to menu item - fix ActionDelegate methods for menu item creation where filter is null --- .../logic/action/ActionDelegate.groovy | 6 +- .../engine/menu/domain/MenuItemBody.java | 2 +- .../engine-processes/dashboard_item.xml | 86 +++++++++---------- .../engine-processes/dashboard_management.xml | 48 +++++------ 4 files changed, 73 insertions(+), 69 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index e478bd253a7..ddf19c524c9 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -2406,7 +2406,11 @@ class ActionDelegate { body.setAllowedRoles(collectRolesForPreferenceItem(allowedRoles)) body.setBannedRoles(collectRolesForPreferenceItem(bannedRoles)) body.setUseTabbedView(true) - body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + if (filter == null) { + body.setView(createLegacyMenuItemViews(new FilterBody(null), caseDefaultHeaders, taskDefaultHeaders)) + } else { + body.setView(createLegacyMenuItemViews(filter, caseDefaultHeaders, taskDefaultHeaders)) + } return createOrUpdateMenuItem(body) } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 1d644b644be..9637f9ddeb8 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -109,7 +109,7 @@ public void setTabName(String name) { public void setView(ViewBody viewBody) { this.view = viewBody; - this.useTabbedView = viewBody.getViewType().isTabbed(); + this.useTabbedView = viewBody == null || viewBody.getViewType().isTabbed(); } /** diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index 33f2ce15d4c..9f39af4dcc8 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -46,7 +46,7 @@ if (item_id.value == "") { return } - def itemWithIdExists = findCaseElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\"" as String) + def itemWithIdExists = findCaseElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\" AND dataSet.is_active.booleanValue:true" as String) if (itemWithIdExists != null) { throw new IllegalArgumentException("Dashboard item with given ID already exists.") } @@ -55,26 +55,26 @@ - preference_items_list - Existing menu items - List of all existing menu items + menu_items_list + Existing menu items + List of all existing menu items autocomplete_dynamic - preference_items_list_get + menu_items_list_get - preference_items_list: f.preference_items_list; + menu_items_list: f.menu_items_list; - def preferenceItemCases = findCasesElastic("processIdentifier:\"menu_item\"" as String) + def menuItemCases = findCasesElastic("processIdentifier:\"menu_item\"" as String) .collectEntries { [(it.stringId): it.dataSet["menu_name"].value] } - change preference_items_list options { preferenceItemCases } + change menu_items_list options { menuItemCases } - preference_items_list_set + menu_items_list_set icon_color: f.icon_color, @@ -82,22 +82,22 @@ font_weight: f.font_weight, inherit_name: f.inherit_name, inherit_icon: f.inherit_icon, - preference_item_taskRef: f.preference_item_taskRef, + menu_item_taskRef: f.menu_item_taskRef, filter_divider: f.filter_divider, - preference_items_list: f.preference_items_list, + menu_items_list: f.menu_items_list, trans: t.configuration; change inherit_icon value { true } change inherit_name value { true } - if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { + if (menu_items_list.value != null && org.bson.types.ObjectId.isValid(menu_items_list.value)) { make [inherit_name, inherit_icon, icon_color, font_color, font_weight], editable on trans when { true } - make [preference_item_taskRef, filter_divider], visible on trans when { true } - def preferenceItemCase = workflowService.findOne(preference_items_list.value) - change preference_item_taskRef value { [preferenceItemCase.tasks.find { it.transition == "item_settings" }?.task] } + make [menu_item_taskRef, filter_divider], visible on trans when { true } + def menuItemCase = workflowService.findOne(menu_items_list.value) + change menu_item_taskRef value { [menuItemCase.tasks.find { it.transition == "item_settings" }?.task] } } else { make [inherit_name, inherit_icon, icon_color, font_color, font_weight], visible on trans when { true } - make [preference_item_taskRef, filter_divider], hidden on trans when { true } - change preference_item_taskRef value { [] } + make [menu_item_taskRef, filter_divider], hidden on trans when { true } + change menu_item_taskRef value { [] } } @@ -115,13 +115,13 @@ item_name: f.item_name, item_icon: f.item_icon, external_icon: f.external_icon, - preference_items_list: f.preference_items_list, + menu_items_list: f.menu_items_list, external_url: f.external_url, is_internal: f.is_internal; change external_icon value { false } if (is_internal.value) { - change preference_items_list value { "" } + change menu_items_list value { "" } } else { change item_icon value { "" } change item_name value { new com.netgrif.application.engine.petrinet.domain.I18nString("") } @@ -135,22 +135,22 @@ item_name: f.item_name, item_icon: f.item_icon, external_icon: f.external_icon, - preference_items_list: f.preference_items_list, + menu_items_list: f.menu_items_list, item_icon_preview: f.item_icon_preview, external_url: f.external_url, inherit_name: f.inherit_name, inherit_icon: f.inherit_icon, - preference_item_taskRef: f.preference_item_taskRef, + menu_item_taskRef: f.menu_item_taskRef, filter_divider: f.filter_divider, trans: t.configuration; if (is_internal.value) { - make [preference_items_list], editable on trans when { true } + make [menu_items_list], editable on trans when { true } make [external_url], hidden on trans when { true } } else { make [item_icon, item_name, external_url, external_icon, font_color, font_weight], editable on trans when { true } make item_icon_preview, visible on trans when { true } - make [inherit_name, inherit_icon, preference_items_list, preference_item_taskRef, filter_divider], hidden on trans when { true } + make [inherit_name, inherit_icon, menu_items_list, menu_item_taskRef, filter_divider], hidden on trans when { true } } @@ -169,7 +169,7 @@ external_icon_set - preference_items_list: f.preference_items_list, + menu_items_list: f.menu_items_list, inherit_icon: f.inherit_icon, external_icon: f.external_icon, icon_color: f.icon_color, @@ -182,7 +182,7 @@ } if (external_icon.value) { make icon_color, visible on trans when { true } - } else if (!(is_internal.value && (preference_items_list.value == null || !org.bson.types.ObjectId.isValid(preference_items_list.value)))) { + } else if (!(is_internal.value && (menu_items_list.value == null || !org.bson.types.ObjectId.isValid(menu_items_list.value)))) { make icon_color, editable on trans when { true } } @@ -272,15 +272,15 @@ item_icon_preview: f.item_icon_preview, external_icon: f.external_icon, item_icon: f.item_icon, - preference_items_list: f.preference_items_list, + menu_items_list: f.menu_items_list, trans: t.configuration; change external_icon value { false } if (inherit_icon.value) { make [item_icon, item_icon_preview, external_icon], visible on trans when { true } - if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { - def preferenceItemCase = workflowService.findOne(preference_items_list.value) - change item_icon value { preferenceItemCase.dataSet["menu_icon"].value } + if (menu_items_list.value != null && org.bson.types.ObjectId.isValid(menu_items_list.value)) { + def menuItemCase = workflowService.findOne(menu_items_list.value) + change item_icon value { menuItemCase.dataSet["menu_icon"].value } } else { change item_icon value { "" } } @@ -304,12 +304,12 @@ inherit_name: f.inherit_name, item_name: f.item_name, - preference_items_list: f.preference_items_list; + menu_items_list: f.menu_items_list; if (inherit_name.value) { - if (preference_items_list.value != null && org.bson.types.ObjectId.isValid(preference_items_list.value)) { - def preferenceItemCase = workflowService.findOne(preference_items_list.value) - change item_name value { preferenceItemCase.dataSet["menu_name"].value } + if (menu_items_list.value != null && org.bson.types.ObjectId.isValid(menu_items_list.value)) { + def menuItemCase = workflowService.findOne(menu_items_list.value) + change item_name value { menuItemCase.dataSet["menu_name"].value } } else { change item_name value { new com.netgrif.application.engine.petrinet.domain.I18nString("") } } @@ -332,7 +332,7 @@ - preference_item_taskRef + menu_item_taskRef </data> <data type="i18n"> @@ -357,8 +357,8 @@ <i18n locale="sk"> <i18nString name="item_id">ID položky panelu</i18nString> <i18nString name="item_id_desc">Unikátny identifikátor položky panelu</i18nString> - <i18nString name="preference_items_list">Existujúce menu položky</i18nString> - <i18nString name="preference_items_list_desc">Zoznam všetkých existujúcich menu položiek</i18nString> + <i18nString name="menu_items_list">Existujúce menu položky</i18nString> + <i18nString name="menu_items_list_desc">Zoznam všetkých existujúcich menu položiek</i18nString> <i18nString name="is_internal">Interná položka</i18nString> <i18nString name="is_internal_desc">Položka interného menu alebo odkaz na externú webovú adresu URL</i18nString> <i18nString name="external_url">Externá adresa URL</i18nString> @@ -384,8 +384,8 @@ <i18n locale="de"> <i18nString name="item_id">Dashboard-Element-ID</i18nString> <i18nString name="item_id_desc">Eindeutiger Bezeichner des Dashboard-Elements</i18nString> - <i18nString name="preference_items_list">Vorhandene Menüpunkte</i18nString> - <i18nString name="preference_items_list_desc">Liste aller vorhandenen Menüpunkte</i18nString> + <i18nString name="menu_items_list">Vorhandene Menüpunkte</i18nString> + <i18nString name="menu_items_list_desc">Liste aller vorhandenen Menüpunkte</i18nString> <i18nString name="is_internal">Interner Posten</i18nString> <i18nString name="is_internal_desc">Interner Menüpunkt oder Link zu externer Website-URL</i18nString> <i18nString name="external_url">Externe URL</i18nString> @@ -464,7 +464,7 @@ <cols>4</cols> <layout>grid</layout> <dataRef> - <id>preference_items_list</id> + <id>menu_items_list</id> <logic> <behavior>editable</behavior> </logic> @@ -623,7 +623,7 @@ </layout> </dataRef> <dataRef> - <id>preference_item_taskRef</id> + <id>menu_item_taskRef</id> <logic> <behavior>hidden</behavior> </logic> @@ -673,19 +673,19 @@ item_id: f.item_id, item_name: f.item_name, is_internal: f.is_internal, - preference_items_list: f.preference_items_list, + menu_items_list: f.menu_items_list, external_url: f.external_url, is_active: f.is_active; if (item_id.value == null || item_id.value == "") { throw new IllegalArgumentException("Dashboard item ID cannot be empty.") } - def itemWithIdExists = findCaseElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\"" as String) + def itemWithIdExists = findCaseElastic("processIdentifier:\"dashboard_item\" AND dataSet.item_id.textValue.keyword:\"${item_id.value}\" AND dataSet.is_active.booleanValue:true" as String) if (itemWithIdExists != null) { throw new IllegalArgumentException("Dashboard item with given ID already exists.") } if (is_internal.value) { - if (preference_items_list.value == null || !org.bson.types.ObjectId.isValid(preference_items_list.value)){ + if (menu_items_list.value == null || !org.bson.types.ObjectId.isValid(menu_items_list.value)){ throw new IllegalArgumentException("Internal menu item must be selected.") } } else { diff --git a/src/main/resources/petriNets/engine-processes/dashboard_management.xml b/src/main/resources/petriNets/engine-processes/dashboard_management.xml index 575e23eb537..8ca434ccee8 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_management.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_management.xml @@ -46,7 +46,7 @@ if (dashboard_id.value == "") { return } - def dashboardWithIdExists = findCaseElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\"" as String) + def dashboardWithIdExists = findCaseElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\" AND dataSet.is_active.booleanValue:true" as String) if (dashboardWithIdExists != null) { throw new IllegalArgumentException("Dashboard with given ID already exists.") } @@ -74,7 +74,7 @@ </component> </data> <data type="enumeration_map" immediate="true"> - <id>dashboard_item_to_preference_item</id> + <id>dashboard_item_to_menu_item</id> <title/> </data> <data type="button"> @@ -89,7 +89,7 @@ <id>order_down_set</id> <actions phase="post"> <action> - dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + dashboard_item_to_menu_item: f.dashboard_item_to_menu_item, items_order: f.items_order, dashboard_item_list: f.dashboard_item_list; @@ -97,19 +97,19 @@ return } def keysDashboard = dashboard_item_list.options.keySet() as List - def keysPreference = dashboard_item_to_preference_item.options.keySet() as List + def keysMenu = dashboard_item_to_menu_item.options.keySet() as List def currentIndex = keysDashboard.indexOf(dashboard_item_list.value) if (currentIndex == keysDashboard.size() - 1) { return } java.util.Collections.swap(keysDashboard, currentIndex, currentIndex + 1) - java.util.Collections.swap(keysPreference, currentIndex, currentIndex + 1) + java.util.Collections.swap(keysMenu, currentIndex, currentIndex + 1) def newOrderDashboard = [:] - def newOrderPreference = [:] + def newOrderMenu = [:] keysDashboard.forEach { newOrderDashboard[it] = dashboard_item_list.options[it]} - keysPreference.forEach { newOrderPreference[it] = dashboard_item_to_preference_item.options[it]} + keysMenu.forEach { newOrderMenu[it] = dashboard_item_to_menu_item.options[it]} change dashboard_item_list options { newOrderDashboard } - change dashboard_item_to_preference_item options { newOrderPreference } + change dashboard_item_to_menu_item options { newOrderMenu } change items_order value { String.join(",", newOrderDashboard.keySet()) } </action> </actions> @@ -127,7 +127,7 @@ <id>order_up_set</id> <actions phase="post"> <action> - dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + dashboard_item_to_menu_item: f.dashboard_item_to_menu_item, items_order: f.items_order, dashboard_item_list: f.dashboard_item_list; @@ -135,19 +135,19 @@ return } def keysDashboard = dashboard_item_list.options.keySet() as List - def keysPreference = dashboard_item_to_preference_item.options.keySet() as List + def keysMenu = dashboard_item_to_menu_item.options.keySet() as List def currentIndex = keysDashboard.indexOf(dashboard_item_list.value) if (currentIndex == 0) { return } java.util.Collections.swap(keysDashboard, currentIndex, currentIndex - 1) - java.util.Collections.swap(keysPreference, currentIndex, currentIndex - 1) + java.util.Collections.swap(keysMenu, currentIndex, currentIndex - 1) def newOrderDashboard = [:] - def newOrderPreference = [:] + def newOrderMenu = [:] keysDashboard.forEach { newOrderDashboard[it] = dashboard_item_list.options[it]} - keysPreference.forEach { newOrderPreference[it] = dashboard_item_to_preference_item.options[it]} + keysMenu.forEach { newOrderMenu[it] = dashboard_item_to_menu_item.options[it]} change dashboard_item_list options { newOrderDashboard } - change dashboard_item_to_preference_item options { newOrderPreference } + change dashboard_item_to_menu_item options { newOrderMenu } change items_order value { String.join(",", newOrderDashboard.keySet()) } </action> </actions> @@ -203,7 +203,7 @@ <id>add_new_item_set</id> <actions phase="post"> <action> - dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + dashboard_item_to_menu_item: f.dashboard_item_to_menu_item, items_order: f.items_order, dashboard_item_list: f.dashboard_item_list, existing_menu_items: f.existing_menu_items; @@ -217,13 +217,13 @@ change items_order value { String.join(",", dashboardItemOptions.keySet()) } def dashboardItemCase = workflowService.findOne(existing_menu_items.value) - def preferenceItemOptions = dashboard_item_to_preference_item.options + def menuItemOptions = dashboard_item_to_menu_item.options if (dashboardItemCase.dataSet["is_internal"].value) { - preferenceItemOptions.put(existing_menu_items.value, dashboardItemCase.dataSet["preference_items_list"].value) + menuItemOptions.put(existing_menu_items.value, dashboardItemCase.dataSet["menu_items_list"].value) } else { - preferenceItemOptions.put(existing_menu_items.value, null) + menuItemOptions.put(existing_menu_items.value, null) } - change dashboard_item_to_preference_item options { preferenceItemOptions } + change dashboard_item_to_menu_item options { menuItemOptions } def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } @@ -242,7 +242,7 @@ <id>remove_new_item_set</id> <actions phase="post"> <action> - dashboard_item_to_preference_item: f.dashboard_item_to_preference_item, + dashboard_item_to_menu_item: f.dashboard_item_to_menu_item, dashboard_item_list: f.dashboard_item_list, items_order: f.items_order, existing_menu_items: f.existing_menu_items; @@ -255,9 +255,9 @@ change dashboard_item_list options { dashboardItemOptions } change items_order value { String.join(",", dashboardItemOptions.keySet()) } - def preferenceItemOptions = dashboard_item_to_preference_item.options - preferenceItemOptions.keySet().removeAll([dashboard_item_list.value]) - change dashboard_item_to_preference_item options { preferenceItemOptions } + def menuItemOptions = dashboard_item_to_menu_item.options + menuItemOptions.keySet().removeAll([dashboard_item_list.value]) + change dashboard_item_to_menu_item options { menuItemOptions } def dashboardItemCases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.is_active.booleanValue:true" as String) .collectEntries { [(it.stringId): it.dataSet["item_name"].value] } @@ -690,7 +690,7 @@ if (dashboard_id.value == null || dashboard_id.value == "") { throw new IllegalArgumentException("Dashboard ID cannot be empty.") } - def dashboardWithIdExists = findCaseElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\"" as String) + def dashboardWithIdExists = findCaseElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_id.textValue.keyword:\"${dashboard_id.value}\" AND dataSet.is_active.booleanValue:true" as String) if (dashboardWithIdExists != null) { throw new IllegalArgumentException("Dashboard with given ID already exists.") } From 7f14cf62c47e162acde4a984207bdda5e7e6a471 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Tue, 18 Feb 2025 09:37:51 +0100 Subject: [PATCH 036/174] Release/6.5.0 --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a39d421e886..b298051e33e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -Full Changelog: [https://github.com/netgrif/application-engine/commits/v6.4.0](https://github.com/netgrif/application-engine/commits/v6.4.0) +Full Changelog: [https://github.com/netgrif/application-engine/commits/v6.5.0](https://github.com/netgrif/application-engine/commits/v6.5.0) + +## [6.5.0](https://github.com/netgrif/application-engine/releases/tag/v6.5.0) (2025-02-18) + +### Added +- [NAE-2033] Welcome dashboard + +### Changed +- [NAE-2034] Open first view +- [NAE-2051] Implement configurable view in menu items +- [NAE-2039] Search in workflow view +- [NAE-2052] Integrate ticket view with menu items ## [6.4.0](https://github.com/netgrif/application-engine/releases/tag/v6.4.0) (2024-12-24) From d6cec3e94549a718a024f9dba4877b0e6d412f68 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Tue, 18 Feb 2025 13:27:43 +0100 Subject: [PATCH 037/174] Release/6.5.0 --- .../application/engine/startup/FilterRunner.groovy | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy index bbdb4ceb1c5..fe45689889b 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy @@ -37,7 +37,15 @@ class FilterRunner extends AbstractOrderedCommandLineRunner { helper.importProcess("Petri net for filters", FILTER_PETRI_NET_IDENTIFIER, FILTER_FILE_NAME) createConfigurationNets() helper.importProcess("Petri net for filter preferences", MENU_NET_IDENTIFIER, MENU_ITEM_FILE_NAME) + createImportFiltersNet() + createExportFiltersNet() + } + + Optional<PetriNet> createImportFiltersNet() { helper.importProcess("Petri net for importing filters", IMPORT_NET_IDENTIFIER, IMPORT_FILTER_FILE_NAME) + } + + Optional<PetriNet> createExportFiltersNet() { helper.importProcess("Petri net for exporting filters", EXPORT_NET_IDENTIFIER, EXPORT_FILTER_FILE_NAME) } From ea16ee2504496ebb5fa9345e97164f03218e5ee6 Mon Sep 17 00:00:00 2001 From: MartinKranec <kranec@netgrif.com> Date: Thu, 10 Apr 2025 09:58:03 +0200 Subject: [PATCH 038/174] [NAE-2063] Action API 6.5.0 - add actions api functions for dashboard management - add dashboard management service --- .../logic/action/ActionDelegate.groovy | 50 +++++-- .../startup/DefaultDashboardRunner.groovy | 27 ++++ .../engine/startup/RunnerController.groovy | 1 + .../domain/dashboard/DashboardItemBody.java | 54 +++++++ .../dashboard/DashboardItemConstants.java | 23 +++ .../dashboard/DashboardManagementBody.java | 71 +++++++++ .../DashboardManagementConstants.java | 18 +++ .../dashboard/DashboardToDataSetOutcome.java | 34 +++++ .../DashboardManagementServiceImpl.java | 137 ++++++++++++++++++ .../DashboardManagementService.java | 20 +++ 10 files changed, 425 insertions(+), 10 deletions(-) create mode 100644 src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardToDataSetOutcome.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index ddf19c524c9..71f81e8fb32 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -26,10 +26,12 @@ import com.netgrif.application.engine.mail.interfaces.IMailService import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants -import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody +import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody +import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.pdf.generator.config.PdfResource @@ -82,6 +84,7 @@ import org.springframework.data.domain.Pageable import java.time.ZoneId import java.util.stream.Collectors + /** * ActionDelegate class contains Actions API methods. */ @@ -197,6 +200,9 @@ class ActionDelegate { @Autowired IMenuItemService menuItemService + @Autowired + DashboardManagementService dashboardManagementService + FrontendActionOutcome Frontend /** @@ -1872,13 +1878,13 @@ class ActionDelegate { * @param item {@link Case} instance of menu_item.xml */ def changeMenuItem(Case item) { - [allowedRoles : { cl -> + [allowedRoles : { cl -> updateMenuItemRoles(item, cl as Closure, MenuItemConstants.FIELD_ALLOWED_ROLES) }, - bannedRoles : { cl -> + bannedRoles : { cl -> updateMenuItemRoles(item, cl as Closure, MenuItemConstants.FIELD_BANNED_ROLES) }, - uri : { cl -> + uri : { cl -> def uri = cl() as String def aCase = useCase if (useCase == null || item.stringId != useCase.stringId) { @@ -1886,32 +1892,32 @@ class ActionDelegate { } moveMenuItem(aCase, uri) }, - title : { cl -> + title : { cl -> def value = cl() I18nString newName = (value instanceof I18nString) ? value : new I18nString(value as String) setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ (MenuItemConstants.FIELD_MENU_NAME): ["type": "i18n", "value": newName] ]) }, - menuIcon : { cl -> + menuIcon : { cl -> def value = cl() setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ (MenuItemConstants.FIELD_MENU_ICON): ["type": "text", "value": value] ]) }, - tabIcon : { cl -> + tabIcon : { cl -> def value = cl() setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ (MenuItemConstants.FIELD_TAB_ICON): ["type": "text", "value": value] ]) }, - useCustomView : { cl -> + useCustomView : { cl -> def value = cl() setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ (MenuItemConstants.FIELD_USE_CUSTOM_VIEW): ["type": "boolean", "value": value] ]) }, - customViewSelector : { cl -> + customViewSelector: { cl -> def value = cl() setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ (MenuItemConstants.FIELD_CUSTOM_VIEW_SELECTOR): ["type": "text", "value": value] @@ -2113,7 +2119,7 @@ class ActionDelegate { caseView.setChainedView(taskView) return caseView - } else if (filterBody.getType() == "Task"){ + } else if (filterBody.getType() == "Task") { ViewBody taskView = new TabbedTaskViewBody() taskView.setFilterBody(filterBody) taskView.setDefaultHeaders(taskDefaultHeaders) @@ -2228,6 +2234,30 @@ class ActionDelegate { return findMenuItem(uri, name) } + Case createDashboardManagement(DashboardManagementBody body) { + return dashboardManagementService.createDashboardManagement(body) + } + + Case createDashboardItem(DashboardItemBody body) { + return dashboardManagementService.createDashboardItem(body) + } + + Case findDashboardManagement(String identifier) { + return dashboardManagementService.findDashboardManagement(identifier) + } + + Case findDashboardItem(String identifier) { + return dashboardManagementService.findDashboardItem(identifier) + } + + Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { + return dashboardManagementService.updateDashboardManagement(managementCase, body) + } + + Case updateDashboardItem(Case itemCase, DashboardItemBody body) { + return dashboardManagementService.updateDashboardItem(itemCase, body) + } + /** * search elastic with string query for first occurrence * @param query string with search conditions diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy new file mode 100644 index 00000000000..485013653f2 --- /dev/null +++ b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy @@ -0,0 +1,27 @@ +package com.netgrif.application.engine.startup + + +import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody +import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService +import com.netgrif.application.engine.petrinet.domain.I18nString +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component + +@Component +class DefaultDashboardRunner extends AbstractOrderedCommandLineRunner { + + @Autowired + private DashboardManagementService dashboardManagementService + + @Override + void run(String... args) throws Exception { + createMainDashboardManagementItem() + } + + def createMainDashboardManagementItem() { + def dashboardItemBody = new DashboardManagementBody("main_dashboard", new I18nString("Main Dashboard")) + dashboardItemBody.setLogoutDashboard(true) + + return dashboardManagementService.createDashboardManagement(dashboardItemBody) + } +} diff --git a/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy b/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy index f58f29e1eea..f25f942344d 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy @@ -26,6 +26,7 @@ class RunnerController { FlushSessionsRunner, MailRunner, PostalCodeImporter, + DefaultDashboardRunner, DemoRunner, QuartzSchedulerRunner, PdfRunner, diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemBody.java new file mode 100644 index 00000000000..3d08716d758 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemBody.java @@ -0,0 +1,54 @@ +package com.netgrif.application.engine.menu.domain.dashboard; + +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@AllArgsConstructor +public class DashboardItemBody { + + private String id; + private String menuItems; + private boolean isInternal; + private String externalUrl; + private String externalIcon; + private String iconColor; + private String itemIcon; + private String fontColor; + private String fontWeight; + private I18nString name; + private boolean inheritIcon; + private boolean inheritName; + private boolean isActive; + + public DashboardItemBody(String id, String menuItems, String itemIcon, I18nString name, boolean isInternal) { + this.id = id; + this.menuItems = menuItems; + this.isInternal = isInternal; + this.itemIcon = itemIcon; + this.name = name; + } + + public ToDataSetOutcome toDataSet() { + ToDataSetOutcome outcome = new ToDataSetOutcome(); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_ID, FieldType.TEXT, this.id); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_MENU_ITEM_LIST, FieldType.ENUMERATION_MAP, this.menuItems); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_IS_INTERNAL, FieldType.BOOLEAN, this.isInternal); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_EXTERNAL_URL, FieldType.TEXT, this.externalUrl); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_EXTERNAL_ICON, FieldType.BOOLEAN, this.externalIcon); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_ICON_COLOR, FieldType.TEXT, this.iconColor); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_ITEM_ICON, FieldType.TEXT, this.itemIcon); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_FONT_COLOR, FieldType.TEXT, this.fontColor); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_FONT_WEIGHT, FieldType.TEXT, this.fontWeight); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_ITEM_NAME, FieldType.I18N, this.name); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_INHERIT_ICON, FieldType.BOOLEAN, this.inheritIcon); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_INHERIT_NAME, FieldType.BOOLEAN, this.inheritName); + outcome.putDataSetEntry(DashboardItemConstants.FIELD_IS_ACTIVE, FieldType.BOOLEAN, this.isActive); + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemConstants.java new file mode 100644 index 00000000000..de9c3b9bcbf --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardItemConstants.java @@ -0,0 +1,23 @@ +package com.netgrif.application.engine.menu.domain.dashboard; + +/** + * Here are declared constants of process dashboard_management.xml. + */ +public class DashboardItemConstants { + public static final String PROCESS_IDENTIFIER = "dashboard_item"; + public static final String TASK_CONFIGURE = "configuration"; + public static final String FIELD_ID= "item_id"; + public static final String FIELD_MENU_ITEM_LIST = "menu_items_list"; + public static final String FIELD_IS_INTERNAL= "is_internal"; + public static final String FIELD_EXTERNAL_URL= "external_url"; + public static final String FIELD_EXTERNAL_ICON= "external_icon"; + public static final String FIELD_ICON_COLOR= "icon_color"; + public static final String FIELD_ITEM_ICON= "item_icon"; + public static final String FIELD_FONT_COLOR= "font_color"; + public static final String FIELD_FONT_WEIGHT= "font_weight"; + public static final String FIELD_ITEM_NAME= "item_name"; + public static final String FIELD_INHERIT_ICON= "inherit_icon"; + public static final String FIELD_INHERIT_NAME= "inherit_name"; + public static final String FIELD_IS_ACTIVE= "is_active"; + +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java new file mode 100644 index 00000000000..67569cdc793 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java @@ -0,0 +1,71 @@ +package com.netgrif.application.engine.menu.domain.dashboard; + +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +@Builder +@AllArgsConstructor +@Data +public class DashboardManagementBody { + /** + * id + */ + private String id; + + /** + * name with translation + */ + private I18nString name; + /** + * logo from assets on frontend + */ + private String logo; + /** + * menuItemId + */ + private String menuItemId; + /** + * should dashboard toolbar contains menu with options. + */ + private boolean simpleDashboard; + /** + * should dashboard toolbar menu with profile. + */ + private boolean profileDashboard; + /** + * should dashboard toolbar contains menu with language selection + */ + private boolean languageDashboard; + /** + * should dashboard toolbar contains logout button. + */ + private boolean logoutDashboard; + + + public DashboardManagementBody(String id, I18nString name) { + this.id = id; + this.name = name; + } + + public ToDataSetOutcome toDataSet() { + DashboardToDataSetOutcome outcome = new DashboardToDataSetOutcome(); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_ID, FieldType.TEXT, this.id); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_NAME, FieldType.I18N, this.name); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LOGO, FieldType.TEXT, this.logo); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_SIMPLE_DASHBOARD, FieldType.BOOLEAN, this.simpleDashboard); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_PROFILE_DASHBOARD, FieldType.BOOLEAN, this.profileDashboard); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LANGUAGE_DASHBOARD, FieldType.BOOLEAN, this.profileDashboard); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LOGOUT_DASHBOARD, FieldType.BOOLEAN, this.logoutDashboard); + if (menuItemId != null) { + outcome.putDataSetEntryWithOptions(DashboardManagementConstants.FIELD_EXISTING_MENU_ITEMS, FieldType.ENUMERATION_MAP, Map.of(this.menuItemId, new I18nString("")), this.menuItemId); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_ADD_NEW_ITEM, FieldType.BUTTON, 1); + } + return outcome; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java new file mode 100644 index 00000000000..a1f1185a331 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java @@ -0,0 +1,18 @@ +package com.netgrif.application.engine.menu.domain.dashboard; + +/** + * Here are declared constants of process dashboard_management.xml. + */ +public class DashboardManagementConstants { + public static final String PROCESS_IDENTIFIER = "dashboard_management"; + public static final String TASK_CONFIGURE = "configure"; + public static final String FIELD_ID = "dashboard_id"; + public static final String FIELD_NAME = "dashboard_name"; + public static final String FIELD_LOGO = "dashboard_logo"; + public static final String FIELD_EXISTING_MENU_ITEMS = "existing_menu_items"; + public static final String FIELD_SIMPLE_DASHBOARD = "simple_dashboard_toolbar"; + public static final String FIELD_PROFILE_DASHBOARD = "profile_dashboard_toolbar"; + public static final String FIELD_LANGUAGE_DASHBOARD = "language_dashboard_toolbar"; + public static final String FIELD_LOGOUT_DASHBOARD = "logout_dashboard_toolbar"; + public static final String FIELD_ADD_NEW_ITEM = "add_new_item"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardToDataSetOutcome.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardToDataSetOutcome.java new file mode 100644 index 00000000000..b02c972122e --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardToDataSetOutcome.java @@ -0,0 +1,34 @@ +package com.netgrif.application.engine.menu.domain.dashboard; + +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +@EqualsAndHashCode(callSuper = true) +@Data +@AllArgsConstructor +public class DashboardToDataSetOutcome extends ToDataSetOutcome { + public void putDataSetEntryWithOptions(String fieldId, FieldType fieldType, @Nullable Map<String, I18nString> options, @Nullable Object fieldValue){ + Map<String, Object> fieldMap = new LinkedHashMap<>(); + + fieldMap.put("type", fieldType.getName()); + if (fieldType.equals(FieldType.MULTICHOICE_MAP)) { + fieldMap.put("value", Set.of()); + } + if (options == null) { + options = new HashMap<>(); + } + fieldMap.put("options", options); + fieldMap.put("value", fieldValue); + this.getDataSet().put(fieldId, fieldMap); + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java new file mode 100644 index 00000000000..fae822e649d --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -0,0 +1,137 @@ +package com.netgrif.application.engine.menu.services; + +import com.netgrif.application.engine.auth.domain.IUser; +import com.netgrif.application.engine.auth.domain.LoggedUser; +import com.netgrif.application.engine.auth.service.interfaces.IUserService; +import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; +import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementConstants; +import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService; +import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.startup.ImportHelper; +import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.Task; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; +import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DashboardManagementServiceImpl implements DashboardManagementService { + + protected final IWorkflowService workflowService; + protected final IUserService userService; + protected final ITaskService taskService; + protected final IDataService dataService; + protected final IPetriNetService petriNetService; + protected final IElasticCaseService elasticCaseService; + + @Override + public Case createDashboardManagement(DashboardManagementBody body) { + Case managementCase; + MenuItemUtils.sanitize(body.getId()); + + managementCase = findDashboardManagement(body.getId()); + if (managementCase != null) { + log.info("Dashboard management with id:{} already exists", body.getId()); + return managementCase; + } + + LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); + managementCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + ToDataSetOutcome outcome = body.toDataSet(); + setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + return managementCase; + } + + @Override + public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExecutableException { + MenuItemUtils.sanitize(body.getId()); + Case itemCase; + + itemCase = findDashboardItem(body.getId()); + if (itemCase != null) { + log.info("Dashboard item with id:{} already exists", body.getId()); + return itemCase; + } + + LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); + itemCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + ToDataSetOutcome outcome = body.toDataSet(); + itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + return itemCase; + } + + @Override + public Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { + MenuItemUtils.sanitize(body.getId()); + ToDataSetOutcome outcome = body.toDataSet(); + setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + return managementCase; + } + + + @Override + public Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException { + MenuItemUtils.sanitize(body.getId()); + ToDataSetOutcome outcome = body.toDataSet(); + itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + return itemCase; + } + + @Override + public Case findDashboardItem(String identifier) { + String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", + DashboardManagementConstants.PROCESS_IDENTIFIER, DashboardManagementConstants.FIELD_ID, identifier); + return findCase(DashboardManagementConstants.PROCESS_IDENTIFIER, query); + } + + @Override + public Case findDashboardManagement(String identifier) { + String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", + DashboardItemConstants.PROCESS_IDENTIFIER, DashboardItemConstants.FIELD_ID, identifier); + return findCase(DashboardItemConstants.PROCESS_IDENTIFIER, query); + } + + protected Case setData(Case useCase, String transId, Map<String, Map<String, Object>> dataSet) { + String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); + return dataService.setData(taskId, ImportHelper.populateDataset((Map) dataSet)).getCase(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected Case setDataWithExecute(Case useCase, String transId, Map<String, Map<String, Object>> dataSet) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); + Task task = taskService.findOne(taskId); + task = taskService.assignTask(task, loggedUser).getTask(); + task = dataService.setData(task, ImportHelper.populateDataset((Map) dataSet)).getTask(); + return taskService.finishTask(task, loggedUser).getCase(); + } + + protected Case findCase(String processIdentifier, String query) { + CaseSearchRequest request = CaseSearchRequest.builder() + .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) + .query(query) + .build(); + Page<Case> resultPage = elasticCaseService.search(java.util.List.of(request), userService.getLoggedOrSystem().transformToLoggedUser(), + PageRequest.of(0, 1), Locale.getDefault(), false); + + return resultPage.hasContent() ? resultPage.getContent().get(0) : null; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java new file mode 100644 index 00000000000..24065b0c9d2 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java @@ -0,0 +1,20 @@ +package com.netgrif.application.engine.menu.services.interfaces; + +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.workflow.domain.Case; + +public interface DashboardManagementService { + Case createDashboardManagement(DashboardManagementBody body); + + Case createDashboardItem(DashboardItemBody body) throws TransitionNotExecutableException; + + Case updateDashboardManagement(Case managementCase, DashboardManagementBody body); + + Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException; + + Case findDashboardManagement(String identifier); + + Case findDashboardItem(String identifier); +} From 103ad7b328a38863fc1798e3ce37ef07ef738054 Mon Sep 17 00:00:00 2001 From: MartinKranec <kranec@netgrif.com> Date: Tue, 15 Apr 2025 14:47:40 +0200 Subject: [PATCH 039/174] [NAE-2063] Action API 6.5.0 - fixes from testing - add deletion of dashboard items and his references from parents - add test xml --- .../dashboard/DashboardManagementBody.java | 39 ++++++++----- .../DashboardManagementConstants.java | 4 +- .../DashboardManagementServiceImpl.java | 29 ++++++++-- .../engine-processes/dashboard_item.xml | 25 +++++++++ .../engine-processes/menu/menu_item.xml | 15 +++++ .../resources/dashboard_management_test.xml | 55 +++++++++++++++++++ 6 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 src/test/resources/dashboard_management_test.xml diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java index 67569cdc793..8f8aa25da75 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementBody.java @@ -6,11 +6,13 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; -import java.util.Map; +import java.util.HashMap; @Builder @AllArgsConstructor +@NoArgsConstructor @Data public class DashboardManagementBody { /** @@ -27,25 +29,29 @@ public class DashboardManagementBody { */ private String logo; /** - * menuItemId + * dashboardItems */ - private String menuItemId; + private HashMap<String, I18nString> dashboardItems; + /** + * mapping for menuItems to DashboardItems + */ + private HashMap<String, I18nString> menuItemsToDashboardItems; /** * should dashboard toolbar contains menu with options. */ - private boolean simpleDashboard; + private boolean simpleDashboard = false; /** * should dashboard toolbar menu with profile. */ - private boolean profileDashboard; + private boolean profileDashboard = false; /** * should dashboard toolbar contains menu with language selection */ - private boolean languageDashboard; + private boolean languageDashboard = false; /** * should dashboard toolbar contains logout button. */ - private boolean logoutDashboard; + private boolean logoutDashboard = false; public DashboardManagementBody(String id, I18nString name) { @@ -55,16 +61,23 @@ public DashboardManagementBody(String id, I18nString name) { public ToDataSetOutcome toDataSet() { DashboardToDataSetOutcome outcome = new DashboardToDataSetOutcome(); - outcome.putDataSetEntry(DashboardManagementConstants.FIELD_ID, FieldType.TEXT, this.id); - outcome.putDataSetEntry(DashboardManagementConstants.FIELD_NAME, FieldType.I18N, this.name); - outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LOGO, FieldType.TEXT, this.logo); + if (this.id != null) { + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_ID, FieldType.TEXT, this.id); + } + if (this.id != null) { + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_NAME, FieldType.I18N, this.name); + } + if (this.logo != null) { + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LOGO, FieldType.TEXT, this.logo); + } outcome.putDataSetEntry(DashboardManagementConstants.FIELD_SIMPLE_DASHBOARD, FieldType.BOOLEAN, this.simpleDashboard); outcome.putDataSetEntry(DashboardManagementConstants.FIELD_PROFILE_DASHBOARD, FieldType.BOOLEAN, this.profileDashboard); outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LANGUAGE_DASHBOARD, FieldType.BOOLEAN, this.profileDashboard); outcome.putDataSetEntry(DashboardManagementConstants.FIELD_LOGOUT_DASHBOARD, FieldType.BOOLEAN, this.logoutDashboard); - if (menuItemId != null) { - outcome.putDataSetEntryWithOptions(DashboardManagementConstants.FIELD_EXISTING_MENU_ITEMS, FieldType.ENUMERATION_MAP, Map.of(this.menuItemId, new I18nString("")), this.menuItemId); - outcome.putDataSetEntry(DashboardManagementConstants.FIELD_ADD_NEW_ITEM, FieldType.BUTTON, 1); + if (this.dashboardItems != null) { + outcome.putDataSetEntryWithOptions(DashboardManagementConstants.FIELD_DASHBOARD_ITEM_LIST, FieldType.ENUMERATION_MAP, this.dashboardItems, ""); + outcome.putDataSetEntryWithOptions(DashboardManagementConstants.FIELD_DASHBOARD_ITEM_TO_MENU_ITEM_LIST, FieldType.ENUMERATION_MAP, this.menuItemsToDashboardItems, ""); + outcome.putDataSetEntry(DashboardManagementConstants.FIELD_ITEMS_ORDER, FieldType.TEXT, String.join(",", this.dashboardItems.keySet())); } return outcome; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java index a1f1185a331..0b922261721 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/dashboard/DashboardManagementConstants.java @@ -9,10 +9,12 @@ public class DashboardManagementConstants { public static final String FIELD_ID = "dashboard_id"; public static final String FIELD_NAME = "dashboard_name"; public static final String FIELD_LOGO = "dashboard_logo"; - public static final String FIELD_EXISTING_MENU_ITEMS = "existing_menu_items"; + public static final String FIELD_DASHBOARD_ITEM_LIST = "dashboard_item_list"; + public static final String FIELD_DASHBOARD_ITEM_TO_MENU_ITEM_LIST = "dashboard_item_to_menu_item"; public static final String FIELD_SIMPLE_DASHBOARD = "simple_dashboard_toolbar"; public static final String FIELD_PROFILE_DASHBOARD = "profile_dashboard_toolbar"; public static final String FIELD_LANGUAGE_DASHBOARD = "language_dashboard_toolbar"; public static final String FIELD_LOGOUT_DASHBOARD = "logout_dashboard_toolbar"; + public static final String FIELD_ITEMS_ORDER = "items_order"; public static final String FIELD_ADD_NEW_ITEM = "add_new_item"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index fae822e649d..a83d938806e 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -12,6 +12,7 @@ import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementConstants; import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; import com.netgrif.application.engine.startup.ImportHelper; @@ -27,6 +28,7 @@ import org.springframework.stereotype.Service; import java.util.Collections; +import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -52,6 +54,15 @@ public Case createDashboardManagement(DashboardManagementBody body) { log.info("Dashboard management with id:{} already exists", body.getId()); return managementCase; } + if (body.getDashboardItems() != null && !body.getDashboardItems().isEmpty()) { + HashMap<String, I18nString> menuItemToDashboardItem = new HashMap<>(); + body.getDashboardItems().keySet().forEach(dashboardItemId -> { + Case dashboardItem = workflowService.findOne(dashboardItemId); + menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); + }); + body.setMenuItemsToDashboardItems(menuItemToDashboardItem); + } + LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); managementCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); @@ -81,6 +92,16 @@ public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExec @Override public Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { MenuItemUtils.sanitize(body.getId()); + + if (body.getDashboardItems() != null && !body.getDashboardItems().isEmpty()) { + HashMap<String, I18nString> menuItemToDashboardItem = new HashMap<>(); + body.getDashboardItems().keySet().forEach(dashboardItemId -> { + Case dashboardItem = workflowService.findOne(dashboardItemId); + menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); + }); + body.setMenuItemsToDashboardItems(menuItemToDashboardItem); + } + ToDataSetOutcome outcome = body.toDataSet(); setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; @@ -98,15 +119,15 @@ public Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws Tr @Override public Case findDashboardItem(String identifier) { String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", - DashboardManagementConstants.PROCESS_IDENTIFIER, DashboardManagementConstants.FIELD_ID, identifier); - return findCase(DashboardManagementConstants.PROCESS_IDENTIFIER, query); + DashboardItemConstants.PROCESS_IDENTIFIER, DashboardItemConstants.FIELD_ID, identifier); + return findCase(DashboardItemConstants.PROCESS_IDENTIFIER, query); } @Override public Case findDashboardManagement(String identifier) { String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", - DashboardItemConstants.PROCESS_IDENTIFIER, DashboardItemConstants.FIELD_ID, identifier); - return findCase(DashboardItemConstants.PROCESS_IDENTIFIER, query); + DashboardManagementConstants.PROCESS_IDENTIFIER, DashboardManagementConstants.FIELD_ID, identifier); + return findCase(DashboardManagementConstants.PROCESS_IDENTIFIER, query); } protected Case setData(Case useCase, String transId, Map<String, Map<String, Object>> dataSet) { diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index 9f39af4dcc8..bca374ae7a9 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -24,6 +24,17 @@ </caseLogic> </roleRef> + <caseEvents> + <event type="delete"> + <id>menu_item_delete</id> + <actions phase="pre"> + <action id="0"> + removeRefFromParent() + </action> + </actions> + </event> + </caseEvents> + <role> <id>system</id> <title>System @@ -33,6 +44,20 @@ Admin + + { -> + def cases = findCasesElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_item_list.keyValue:\"$useCase.stringId\"") + cases.each { refCase -> + def refTask = refCase.tasks.find { it.transition == "configuration" }.task + setData(refTask,[ + "dashboard_item_list": ["type": "text", "value": ""], + "dashboard_item_taskRef": ["type": "taskRef", "value": []] + ]) + } + + } + + item_id Dashboard item ID diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 327dcc656c6..0d6466aea4c 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -35,6 +35,7 @@ menu_item_delete + removeRefFromParent() removeItemChildCases() removeViewCase() @@ -80,6 +81,20 @@ } } + + { -> + def cases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.menu_items_list.keyValue:\"$useCase.stringId\"") + print(cases.size()) + cases.each { refCase -> + def refTask = refCase.tasks.find { it.transition == "configuration" }.task + setData(refTask,[ + "menu_items_list": ["type": "text", "value": ""], + "menu_item_taskRef": ["type": "taskRef", "value": []] + ]) + } + + } + { boolean useTabIcon, boolean useTabbedView, String transId = "item_settings" -> def settingsTrans = useCase.petriNet.transitions[transId] diff --git a/src/test/resources/dashboard_management_test.xml b/src/test/resources/dashboard_management_test.xml new file mode 100644 index 00000000000..390b648be8d --- /dev/null +++ b/src/test/resources/dashboard_management_test.xml @@ -0,0 +1,55 @@ + + menu_import + MEI + Menu import + device_hub + true + true + false + + + new_model_upload + + + + + + + + \ No newline at end of file From e51f1f6476bddbd160f8757ff945b7bffa40c7dd Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Tue, 15 Apr 2025 14:50:45 +0200 Subject: [PATCH 040/174] [NAE-2063] Action API 6.5.0 - refactor --- .../DashboardManagementServiceImpl.java | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index a83d938806e..ead6a9d1801 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -54,16 +54,7 @@ public Case createDashboardManagement(DashboardManagementBody body) { log.info("Dashboard management with id:{} already exists", body.getId()); return managementCase; } - if (body.getDashboardItems() != null && !body.getDashboardItems().isEmpty()) { - HashMap menuItemToDashboardItem = new HashMap<>(); - body.getDashboardItems().keySet().forEach(dashboardItemId -> { - Case dashboardItem = workflowService.findOne(dashboardItemId); - menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); - }); - body.setMenuItemsToDashboardItems(menuItemToDashboardItem); - } - - + addReferencedMenuItems(body); LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); managementCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); @@ -92,16 +83,7 @@ public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExec @Override public Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { MenuItemUtils.sanitize(body.getId()); - - if (body.getDashboardItems() != null && !body.getDashboardItems().isEmpty()) { - HashMap menuItemToDashboardItem = new HashMap<>(); - body.getDashboardItems().keySet().forEach(dashboardItemId -> { - Case dashboardItem = workflowService.findOne(dashboardItemId); - menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); - }); - body.setMenuItemsToDashboardItems(menuItemToDashboardItem); - } - + addReferencedMenuItems(body); ToDataSetOutcome outcome = body.toDataSet(); setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; @@ -155,4 +137,15 @@ protected Case findCase(String processIdentifier, String query) { return resultPage.hasContent() ? resultPage.getContent().get(0) : null; } + + private void addReferencedMenuItems(DashboardManagementBody body) { + if (body.getDashboardItems() != null && !body.getDashboardItems().isEmpty()) { + HashMap menuItemToDashboardItem = new HashMap<>(); + body.getDashboardItems().keySet().forEach(dashboardItemId -> { + Case dashboardItem = workflowService.findOne(dashboardItemId); + menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); + }); + body.setMenuItemsToDashboardItems(menuItemToDashboardItem); + } + } } From 77a175ad4d9c4e1f852ebfa5c7345104cdd3858b Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Tue, 15 Apr 2025 16:05:23 +0200 Subject: [PATCH 041/174] [NAE-2063] Action API 6.5.0 - change process identifier in test process - refresh case context in some methods --- .../engine/menu/services/DashboardManagementServiceImpl.java | 4 ++-- src/test/resources/dashboard_management_test.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index ead6a9d1801..428c323dc4e 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -58,7 +58,7 @@ public Case createDashboardManagement(DashboardManagementBody body) { LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); managementCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); - setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + managementCase = setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; } @@ -85,7 +85,7 @@ public Case updateDashboardManagement(Case managementCase, DashboardManagementBo MenuItemUtils.sanitize(body.getId()); addReferencedMenuItems(body); ToDataSetOutcome outcome = body.toDataSet(); - setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + managementCase = setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; } diff --git a/src/test/resources/dashboard_management_test.xml b/src/test/resources/dashboard_management_test.xml index 390b648be8d..d3f1ae15906 100644 --- a/src/test/resources/dashboard_management_test.xml +++ b/src/test/resources/dashboard_management_test.xml @@ -1,7 +1,7 @@ - menu_import + dashboard_import MEI - Menu import + Dashboard import device_hub true true From 73fb7138a76d82b1c8b27e43ca11b1bfbefd2541 Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Wed, 16 Apr 2025 08:05:37 +0200 Subject: [PATCH 042/174] [NAE-2063] Action API 6.5.0 - add documentation for serviceimpl --- .../DashboardManagementServiceImpl.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index 428c323dc4e..999bdbab6e0 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -44,6 +44,14 @@ public class DashboardManagementServiceImpl implements DashboardManagementServic protected final IPetriNetService petriNetService; protected final IElasticCaseService elasticCaseService; + + /** + * Creates a new dashboard management case if it does not already exist. + * If a case with the same ID is found, it is returned instead of creating a new one. + * + * @param body The {@link DashboardManagementBody} containing data for the dashboard management. + * @return The created or existing {@link Case} representing the dashboard management. + */ @Override public Case createDashboardManagement(DashboardManagementBody body) { Case managementCase; @@ -62,6 +70,14 @@ public Case createDashboardManagement(DashboardManagementBody body) { return managementCase; } + /** + * Creates a new dashboard item case if it does not already exist. + * If a case with the same ID is found, it is returned instead of creating a new one. + * + * @param body The {@link DashboardItemBody} containing data for the dashboard item. + * @return The created or existing {@link Case} representing the dashboard item. + * @throws TransitionNotExecutableException if the task transition is not executable. + */ @Override public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExecutableException { MenuItemUtils.sanitize(body.getId()); @@ -80,6 +96,13 @@ public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExec return itemCase; } + /** + * Updates an existing dashboard management case with new data. + * + * @param managementCase The existing {@link Case} to update. + * @param body The {@link DashboardManagementBody} containing updated data. + * @return The updated {@link Case} representing the dashboard management. + */ @Override public Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { MenuItemUtils.sanitize(body.getId()); @@ -90,6 +113,14 @@ public Case updateDashboardManagement(Case managementCase, DashboardManagementBo } + /** + * Updates an existing dashboard item case with new data. + * + * @param itemCase The existing {@link Case} to update. + * @param body The {@link DashboardItemBody} containing updated data. + * @return The updated {@link Case} representing the dashboard item. + * @throws TransitionNotExecutableException if the task transition is not executable. + */ @Override public Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException { MenuItemUtils.sanitize(body.getId()); @@ -98,6 +129,13 @@ public Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws Tr return itemCase; } + + /** + * Finds an existing dashboard item case by its identifier. + * + * @param identifier The unique identifier of the dashboard item. + * @return The {@link Case} representing the dashboard item, or null if not found. + */ @Override public Case findDashboardItem(String identifier) { String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", @@ -105,6 +143,12 @@ public Case findDashboardItem(String identifier) { return findCase(DashboardItemConstants.PROCESS_IDENTIFIER, query); } + /** + * Finds an existing dashboard management case by its identifier. + * + * @param identifier The unique identifier of the dashboard management. + * @return The {@link Case} representing the dashboard management, or null if not found. + */ @Override public Case findDashboardManagement(String identifier) { String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", From 318c517690e09872fa8b19e4c253f395735f2d82 Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Wed, 16 Apr 2025 08:06:19 +0200 Subject: [PATCH 043/174] [NAE-2063] Action API 6.5.0 - format --- .../engine/menu/services/DashboardManagementServiceImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index 999bdbab6e0..214e048da3d 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -112,7 +112,6 @@ public Case updateDashboardManagement(Case managementCase, DashboardManagementBo return managementCase; } - /** * Updates an existing dashboard item case with new data. * From 8478fdb21c7709df4b4bdfc75d31bc3ef46359db Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Wed, 16 Apr 2025 14:37:04 +0200 Subject: [PATCH 044/174] [NAE-2063] Action API 6.5.0 - refactor from PR comments --- .../logic/action/ActionDelegate.groovy | 10 +- .../startup/DefaultDashboardRunner.groovy | 2 +- .../services/DashboardItemServiceImpl.java | 124 ++++++++++++++++++ .../DashboardManagementServiceImpl.java | 80 ++--------- .../interfaces/DashboardItemService.java | 14 ++ .../DashboardManagementService.java | 10 +- .../engine-processes/menu/menu_item.xml | 5 +- 7 files changed, 162 insertions(+), 83 deletions(-) create mode 100644 src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 71f81e8fb32..26791594b3e 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -31,6 +31,7 @@ import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewB import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody +import com.netgrif.application.engine.menu.services.interfaces.DashboardItemService import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService @@ -203,6 +204,9 @@ class ActionDelegate { @Autowired DashboardManagementService dashboardManagementService + @Autowired + DashboardItemService dashboardItemService + FrontendActionOutcome Frontend /** @@ -2239,7 +2243,7 @@ class ActionDelegate { } Case createDashboardItem(DashboardItemBody body) { - return dashboardManagementService.createDashboardItem(body) + return dashboardItemService.getOrCreate(body) } Case findDashboardManagement(String identifier) { @@ -2247,7 +2251,7 @@ class ActionDelegate { } Case findDashboardItem(String identifier) { - return dashboardManagementService.findDashboardItem(identifier) + return dashboardItemService.findById(identifier) } Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { @@ -2255,7 +2259,7 @@ class ActionDelegate { } Case updateDashboardItem(Case itemCase, DashboardItemBody body) { - return dashboardManagementService.updateDashboardItem(itemCase, body) + return dashboardItemService.update(itemCase, body) } /** diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy index 485013653f2..7a2fe188454 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy @@ -19,7 +19,7 @@ class DefaultDashboardRunner extends AbstractOrderedCommandLineRunner { } def createMainDashboardManagementItem() { - def dashboardItemBody = new DashboardManagementBody("main_dashboard", new I18nString("Main Dashboard")) + def dashboardItemBody = new DashboardManagementBody("main_dashboard", new I18nString("Main Dashboard",Map.of("sk","Hlavný Dashboard","en","Haupt-Dashboard"))) dashboardItemBody.setLogoutDashboard(true) return dashboardManagementService.createDashboardManagement(dashboardItemBody) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java new file mode 100644 index 00000000000..cd8b2c3a09c --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java @@ -0,0 +1,124 @@ +package com.netgrif.application.engine.menu.services; + +import com.netgrif.application.engine.auth.domain.IUser; +import com.netgrif.application.engine.auth.domain.LoggedUser; +import com.netgrif.application.engine.auth.service.interfaces.IUserService; +import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; +import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; +import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; +import com.netgrif.application.engine.menu.services.interfaces.DashboardItemService; +import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.startup.ImportHelper; +import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.Task; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; +import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DashboardItemServiceImpl implements DashboardItemService { + + protected final IWorkflowService workflowService; + protected final IUserService userService; + protected final ITaskService taskService; + protected final IDataService dataService; + protected final IPetriNetService petriNetService; + protected final IElasticCaseService elasticCaseService; + + /** + * Creates a new dashboard item case if it does not already exist. + * If a case with the same ID is found, it is returned instead of creating a new one. + * + * @param body The {@link DashboardItemBody} containing data for the dashboard item. + * @return The created or existing {@link Case} representing the dashboard item. + * @throws TransitionNotExecutableException if the task transition is not executable. + */ + @Override + public Case getOrCreate(DashboardItemBody body) throws TransitionNotExecutableException { + MenuItemUtils.sanitize(body.getId()); + Case itemCase; + + itemCase = findById(body.getId()); + if (itemCase != null) { + log.info("Dashboard item with id:{} already exists", body.getId()); + return itemCase; + } + + LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); + itemCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); + ToDataSetOutcome outcome = body.toDataSet(); + itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + return itemCase; + } + + + /** + * Updates an existing dashboard item case with new data. + * + * @param itemCase The existing {@link Case} to update. + * @param body The {@link DashboardItemBody} containing updated data. + * @return The updated {@link Case} representing the dashboard item. + * @throws TransitionNotExecutableException if the task transition is not executable. + */ + @Override + public Case update(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException { + MenuItemUtils.sanitize(body.getId()); + ToDataSetOutcome outcome = body.toDataSet(); + itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + return itemCase; + } + + /** + * Finds an existing dashboard item case by its identifier. + * + * @param identifier The unique identifier of the dashboard item. + * @return The {@link Case} representing the dashboard item, or null if not found. + */ + @Override + public Case findById(String identifier) { + String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", + DashboardItemConstants.PROCESS_IDENTIFIER, DashboardItemConstants.FIELD_ID, identifier); + return findCase(DashboardItemConstants.PROCESS_IDENTIFIER, query); + } + + protected Case setData(Case useCase, String transId, Map> dataSet) { + String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); + return dataService.setData(taskId, ImportHelper.populateDataset((Map) dataSet)).getCase(); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected Case setDataWithExecute(Case useCase, String transId, Map> dataSet) throws TransitionNotExecutableException { + IUser loggedUser = userService.getLoggedOrSystem(); + String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); + Task task = taskService.findOne(taskId); + task = taskService.assignTask(task, loggedUser).getTask(); + task = dataService.setData(task, ImportHelper.populateDataset((Map) dataSet)).getTask(); + return taskService.finishTask(task, loggedUser).getCase(); + } + + protected Case findCase(String processIdentifier, String query) { + CaseSearchRequest request = CaseSearchRequest.builder() + .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) + .query(query) + .build(); + Page resultPage = elasticCaseService.search(java.util.List.of(request), userService.getLoggedOrSystem().transformToLoggedUser(), + PageRequest.of(0, 1), Locale.getDefault(), false); + + return resultPage.hasContent() ? resultPage.getContent().get(0) : null; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java index 214e048da3d..c35ce0267f8 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java @@ -6,7 +6,6 @@ import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; -import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementConstants; @@ -53,7 +52,7 @@ public class DashboardManagementServiceImpl implements DashboardManagementServic * @return The created or existing {@link Case} representing the dashboard management. */ @Override - public Case createDashboardManagement(DashboardManagementBody body) { + public Case createDashboardManagement(DashboardManagementBody body) throws TransitionNotExecutableException { Case managementCase; MenuItemUtils.sanitize(body.getId()); @@ -66,36 +65,10 @@ public Case createDashboardManagement(DashboardManagementBody body) { LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); managementCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardManagementConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); ToDataSetOutcome outcome = body.toDataSet(); - managementCase = setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + managementCase = setDataWithExecute(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; } - /** - * Creates a new dashboard item case if it does not already exist. - * If a case with the same ID is found, it is returned instead of creating a new one. - * - * @param body The {@link DashboardItemBody} containing data for the dashboard item. - * @return The created or existing {@link Case} representing the dashboard item. - * @throws TransitionNotExecutableException if the task transition is not executable. - */ - @Override - public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExecutableException { - MenuItemUtils.sanitize(body.getId()); - Case itemCase; - - itemCase = findDashboardItem(body.getId()); - if (itemCase != null) { - log.info("Dashboard item with id:{} already exists", body.getId()); - return itemCase; - } - - LoggedUser loggedUser = userService.getLoggedOrSystem().transformToLoggedUser(); - itemCase = workflowService.createCase(petriNetService.getNewestVersionByIdentifier(DashboardItemConstants.PROCESS_IDENTIFIER).getStringId(), body.getName().getDefaultValue(), "", loggedUser).getCase(); - ToDataSetOutcome outcome = body.toDataSet(); - itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); - return itemCase; - } - /** * Updates an existing dashboard management case with new data. * @@ -104,44 +77,14 @@ public Case createDashboardItem(DashboardItemBody body) throws TransitionNotExec * @return The updated {@link Case} representing the dashboard management. */ @Override - public Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) { + public Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) throws TransitionNotExecutableException { MenuItemUtils.sanitize(body.getId()); addReferencedMenuItems(body); ToDataSetOutcome outcome = body.toDataSet(); - managementCase = setData(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); + managementCase = setDataWithExecute(managementCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); return managementCase; } - /** - * Updates an existing dashboard item case with new data. - * - * @param itemCase The existing {@link Case} to update. - * @param body The {@link DashboardItemBody} containing updated data. - * @return The updated {@link Case} representing the dashboard item. - * @throws TransitionNotExecutableException if the task transition is not executable. - */ - @Override - public Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException { - MenuItemUtils.sanitize(body.getId()); - ToDataSetOutcome outcome = body.toDataSet(); - itemCase = setDataWithExecute(itemCase, DashboardItemConstants.TASK_CONFIGURE, outcome.getDataSet()); - return itemCase; - } - - - /** - * Finds an existing dashboard item case by its identifier. - * - * @param identifier The unique identifier of the dashboard item. - * @return The {@link Case} representing the dashboard item, or null if not found. - */ - @Override - public Case findDashboardItem(String identifier) { - String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"", - DashboardItemConstants.PROCESS_IDENTIFIER, DashboardItemConstants.FIELD_ID, identifier); - return findCase(DashboardItemConstants.PROCESS_IDENTIFIER, query); - } - /** * Finds an existing dashboard management case by its identifier. * @@ -182,13 +125,14 @@ protected Case findCase(String processIdentifier, String query) { } private void addReferencedMenuItems(DashboardManagementBody body) { - if (body.getDashboardItems() != null && !body.getDashboardItems().isEmpty()) { - HashMap menuItemToDashboardItem = new HashMap<>(); - body.getDashboardItems().keySet().forEach(dashboardItemId -> { - Case dashboardItem = workflowService.findOne(dashboardItemId); - menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); - }); - body.setMenuItemsToDashboardItems(menuItemToDashboardItem); + if (body.getDashboardItems() == null || body.getDashboardItems().isEmpty()) { + return; } + HashMap menuItemToDashboardItem = new HashMap<>(); + body.getDashboardItems().keySet().forEach(dashboardItemId -> { + Case dashboardItem = workflowService.findOne(dashboardItemId); + menuItemToDashboardItem.put(dashboardItemId, new I18nString(String.valueOf(dashboardItem.getFieldValue(DashboardItemConstants.FIELD_MENU_ITEM_LIST)))); + }); + body.setMenuItemsToDashboardItems(menuItemToDashboardItem); } } diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java new file mode 100644 index 00000000000..6555ad2c04f --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java @@ -0,0 +1,14 @@ +package com.netgrif.application.engine.menu.services.interfaces; + +import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.workflow.domain.Case; + +public interface DashboardItemService { + + Case getOrCreate(DashboardItemBody body) throws TransitionNotExecutableException; + + Case update(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException; + + Case findById(String identifier); +} diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java index 24065b0c9d2..01efb79d055 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java @@ -1,20 +1,14 @@ package com.netgrif.application.engine.menu.services.interfaces; -import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; public interface DashboardManagementService { - Case createDashboardManagement(DashboardManagementBody body); + Case createDashboardManagement(DashboardManagementBody body) throws TransitionNotExecutableException; - Case createDashboardItem(DashboardItemBody body) throws TransitionNotExecutableException; - - Case updateDashboardManagement(Case managementCase, DashboardManagementBody body); - - Case updateDashboardItem(Case itemCase, DashboardItemBody body) throws TransitionNotExecutableException; + Case updateDashboardManagement(Case managementCase, DashboardManagementBody body) throws TransitionNotExecutableException; Case findDashboardManagement(String identifier); - Case findDashboardItem(String identifier); } diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 0d6466aea4c..089593e0e49 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -35,7 +35,7 @@ menu_item_delete - removeRefFromParent() + removeRefFromDashboardItem() removeItemChildCases() removeViewCase() @@ -81,10 +81,9 @@ } } - + { -> def cases = findCasesElastic("processIdentifier:\"dashboard_item\" AND dataSet.menu_items_list.keyValue:\"$useCase.stringId\"") - print(cases.size()) cases.each { refCase -> def refTask = refCase.tasks.find { it.transition == "configuration" }.task setData(refTask,[ From 8875df0684f85241ee1b856b4c9fe2ad29b762dd Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Wed, 16 Apr 2025 14:38:34 +0200 Subject: [PATCH 045/174] [NAE-2063] Action API 6.5.0 - fix --- .../application/engine/startup/DefaultDashboardRunner.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy index 7a2fe188454..7e5475ef098 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy @@ -19,7 +19,7 @@ class DefaultDashboardRunner extends AbstractOrderedCommandLineRunner { } def createMainDashboardManagementItem() { - def dashboardItemBody = new DashboardManagementBody("main_dashboard", new I18nString("Main Dashboard",Map.of("sk","Hlavný Dashboard","en","Haupt-Dashboard"))) + def dashboardItemBody = new DashboardManagementBody("main_dashboard", new I18nString("Main Dashboard",Map.of("sk","Hlavný Dashboard","de","Haupt-Dashboard"))) dashboardItemBody.setLogoutDashboard(true) return dashboardManagementService.createDashboardManagement(dashboardItemBody) From 1e41d468b13f622c6caed2f1e7f9a74c5ca1d79d Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Wed, 16 Apr 2025 15:31:37 +0200 Subject: [PATCH 046/174] [NAE-2063] Action API 6.5.0 - update action that deletes references --- .../petriNets/engine-processes/dashboard_item.xml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index bca374ae7a9..d54ab381d93 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -46,12 +46,19 @@ { -> - def cases = findCasesElastic("processIdentifier:\"dashboard_management\" AND dataSet.dashboard_item_list.keyValue:\"$useCase.stringId\"") + def cases = findCasesElastic("processIdentifier:\"dashboard_management\" AND dataSet.items_order.textValue:\"$useCase.stringId\"") cases.each { refCase -> def refTask = refCase.tasks.find { it.transition == "configuration" }.task + def itemsOrder = refCase.dataSet["items_order"].value.split(",") as ArrayList + itemsOrder.remove(useCase.stringId) + + def dashboardItemsListOptions = refCase.dataSet["dashboard_item_list"].options + dashboardItemsListOptions.remove(useCase.stringId) + setData(refTask,[ "dashboard_item_list": ["type": "text", "value": ""], - "dashboard_item_taskRef": ["type": "taskRef", "value": []] + "existing_menu_items": ["type": "existing_menu_items", "value": "", "options": dashboardItemsListOptions], + "items_order": ["type": "text", "value": itemsOrder.join(",")] ]) } From 693438cdde8f9eca9ccbf10e329d65db8f5b91a4 Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Wed, 16 Apr 2025 15:37:31 +0200 Subject: [PATCH 047/174] [NAE-2063] Action API 6.5.0 - update action that deletes references --- .../resources/petriNets/engine-processes/dashboard_item.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/dashboard_item.xml b/src/main/resources/petriNets/engine-processes/dashboard_item.xml index d54ab381d93..efea2de1fa6 100644 --- a/src/main/resources/petriNets/engine-processes/dashboard_item.xml +++ b/src/main/resources/petriNets/engine-processes/dashboard_item.xml @@ -56,8 +56,7 @@ dashboardItemsListOptions.remove(useCase.stringId) setData(refTask,[ - "dashboard_item_list": ["type": "text", "value": ""], - "existing_menu_items": ["type": "existing_menu_items", "value": "", "options": dashboardItemsListOptions], + "dashboard_item_list": ["type": "enumeration_map", "value": "", "options": dashboardItemsListOptions], "items_order": ["type": "text", "value": itemsOrder.join(",")] ]) } From 1c98631ac9fce00cc71d3e9c61bd8cc70f906148 Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 17 Apr 2025 08:06:36 +0200 Subject: [PATCH 048/174] [NAE-2063] Action API 6.5.0 - fix field type in menu_item.xml --- .../resources/petriNets/engine-processes/menu/menu_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 089593e0e49..8fe4ae4c9f7 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -87,7 +87,7 @@ cases.each { refCase -> def refTask = refCase.tasks.find { it.transition == "configuration" }.task setData(refTask,[ - "menu_items_list": ["type": "text", "value": ""], + "menu_items_list": ["type": "enumeration_map", "value": ""], "menu_item_taskRef": ["type": "taskRef", "value": []] ]) } From 1171c1d5214e5b56db560cb95bf35c5dc80d7352 Mon Sep 17 00:00:00 2001 From: MartinKranec Date: Fri, 9 May 2025 09:53:25 +0200 Subject: [PATCH 049/174] [NAE-2099] MenuItemService.appendChildCaseIdInDataSet not setting hasChildren correctly - fix condition --- .../engine/menu/services/MenuItemService.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index eb6a051546d..23f18825609 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -351,7 +351,7 @@ public Case duplicateItem(Case originItem, I18nString newTitle, String newIdenti dataSet.put(MenuItemConstants.FIELD_DUPLICATE_TITLE, Map.of("type", FieldType.I18N.getName(), "value", new I18nString(""))); dataSet.put(MenuItemConstants.FIELD_DUPLICATE_IDENTIFIER, Map.of("type", FieldType.TEXT.getName(), - "value","")); + "value", "")); dataSet.put(MenuItemConstants.FIELD_MENU_NAME, Map.of("type", FieldType.I18N.getName(), "value", newTitle)); dataSet.put(MenuItemConstants.FIELD_TAB_NAME, Map.of("type", FieldType.I18N.getName(), @@ -520,7 +520,7 @@ protected void removeView(Case viewCase) { protected Case handleFilter(Case filterCase, FilterBody body) throws TransitionNotExecutableException { if (mustCreateFilter(filterCase, body)) { return createFilter(body); - } else if (mustUpdateFilter(filterCase, body)){ + } else if (mustUpdateFilter(filterCase, body)) { return updateFilter(filterCase, body); } else { return filterCase; @@ -600,6 +600,7 @@ protected Case getOrCreateFolderItem(String uri) throws TransitionNotExecutableE protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body) throws TransitionNotExecutableException { return getOrCreateFolderRecursive(node, body, null); } + protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case childFolderCase) throws TransitionNotExecutableException { IUser loggedUser = userService.getLoggedOrSystem(); Case folderCase = findFolderCase(node); @@ -636,15 +637,15 @@ protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case protected void appendChildCaseIdInDataSet(Case folderCase, String childItemCaseId, Map> dataSet) { List childIds = MenuItemUtils.getCaseIdsFromCaseRef(folderCase, MenuItemConstants.FIELD_CHILD_ITEM_IDS); if (childIds == null || childIds.isEmpty()) { - dataSet.put(MenuItemConstants.FIELD_CHILD_ITEM_IDS, Map.of("type", FieldType.CASE_REF.getName(), - "value", List.of(childItemCaseId))); + childIds = List.of(childItemCaseId); } else { childIds.add(childItemCaseId); - dataSet.put(MenuItemConstants.FIELD_CHILD_ITEM_IDS, Map.of("type", FieldType.CASE_REF.getName(), - "value", childIds)); } + + dataSet.put(MenuItemConstants.FIELD_CHILD_ITEM_IDS, Map.of("type", FieldType.CASE_REF.getName(), + "value", childIds)); dataSet.put(MenuItemConstants.FIELD_HAS_CHILDREN, Map.of("type", FieldType.BOOLEAN.getName(), - "value", MenuItemUtils.hasFolderChildren(folderCase))); + "value", !childIds.isEmpty())); } protected void appendChildCaseIdInMemory(Case folderCase, String childItemCaseId) { @@ -673,7 +674,7 @@ protected void addConfigurationIntoDataSet(Case configurationCase, Map> dataSet) { From 986650036d155dcda16b477bd950cdb6e619ad6d Mon Sep 17 00:00:00 2001 From: Machac Date: Thu, 26 Jun 2025 13:56:56 +0200 Subject: [PATCH 050/174] release 6.5.0-rc.1 --- .github/workflows/release-build.yml | 2 +- pom.xml | 70 ++++++++++++++++------------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 2485befacbe..d76764a7297 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -142,7 +142,7 @@ jobs: with: java-version: 11 distribution: 'adopt' - server-id: ossrh + server-id: central server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD diff --git a/pom.xml b/pom.xml index 332d1595bfb..83fe2aaf749 100644 --- a/pom.xml +++ b/pom.xml @@ -66,31 +66,31 @@ https://sonarcloud.io - - - oss.snapshots - OSSRH SNAPSHOT - https://s01.oss.sonatype.org/content/repositories/snapshots - - false - - - true - - - - mvnrepository1 - https://maven.imagej.net/content/repositories/public/ - - - mvnrepository2 - https://repo.spring.io/plugins-release/ - - - mulesoft - https://repository.mulesoft.org/nexus/content/repositories/public/ - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -435,7 +435,7 @@ com.netgrif quartz-mongodb-connector - 1.0.0-SNAPSHOT + 1.0.0 @@ -860,14 +860,14 @@ ossrh-publish - ossrh + central Central Repository OSSRH - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + https://central.sonatype.com/ - ossrh + central Central Repository OSSRG Snapshots - https://s01.oss.sonatype.org/content/repositories/snapshots/ + https://central.sonatype.com/repository/maven-snapshots/ @@ -907,6 +907,16 @@ + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + + central + required + + From 76b93bd1ddf4eed43a511e871588f8bd09a74290 Mon Sep 17 00:00:00 2001 From: Machac Date: Thu, 26 Jun 2025 14:10:23 +0200 Subject: [PATCH 051/174] release 6.5.0-rc.1 - QRcode deprecated --- pom.xml | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 83fe2aaf749..5b79a2f6768 100644 --- a/pom.xml +++ b/pom.xml @@ -66,31 +66,31 @@ https://sonarcloud.io - - - - - - - - - - - - - - - - - - - - - - - - - + + + oss.snapshots + OSSRH SNAPSHOT + https://s01.oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + mvnrepository1 + https://maven.imagej.net/content/repositories/public/ + + + mvnrepository2 + https://repo.spring.io/plugins-release/ + + + mulesoft + https://repository.mulesoft.org/nexus/content/repositories/public/ + + From 7f2ffa4b275f73c06b683dec554319ddf75feffc Mon Sep 17 00:00:00 2001 From: Machac Date: Tue, 25 Nov 2025 14:25:38 +0100 Subject: [PATCH 052/174] Release 6.5.0-RC.3 Update MinIO image reference to `bawix/minio:2022` in workflows and docker-compose. --- .github/workflows/master-build.yml | 2 +- .github/workflows/pr-build.yml | 2 +- .github/workflows/release-build.yml | 2 +- docker-compose.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/master-build.yml b/.github/workflows/master-build.yml index 07e94d9276d..c489b2864da 100644 --- a/.github/workflows/master-build.yml +++ b/.github/workflows/master-build.yml @@ -53,7 +53,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: docker.io/bitnami/minio:2022 + image: bawix/minio:2022 ports: - 9000:9000 - 9001:9001 diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 975cff98daf..243aff3a3bc 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -52,7 +52,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: docker.io/bitnami/minio:2022 + image: bawix/minio:2022 ports: - 9000:9000 - 9001:9001 diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index d76764a7297..e6b53bba28c 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -64,7 +64,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: docker.io/bitnami/minio:2022 + image: bawix/minio:2022 ports: - 9000:9000 - 9001:9001 diff --git a/docker-compose.yml b/docker-compose.yml index de15910fffb..20ae8727115 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,7 +39,7 @@ services: ports: - "6379:6379" minio: - image: docker.io/bitnami/minio:2022 + image: bawix/minio:2022 ports: - '9000:9000' - '9001:9001' From 97cbe94ad366051ee1dab672e56bf844228c7585 Mon Sep 17 00:00:00 2001 From: Machac Date: Tue, 25 Nov 2025 15:54:40 +0100 Subject: [PATCH 053/174] Release 6.5.0-RC.3 Update MinIO image reference to `bawix/minio:2022` in workflows and docker-compose. --- .github/workflows/master-build.yml | 2 +- .github/workflows/pr-build.yml | 2 +- .github/workflows/release-build.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/master-build.yml b/.github/workflows/master-build.yml index c489b2864da..1e9711a16ff 100644 --- a/.github/workflows/master-build.yml +++ b/.github/workflows/master-build.yml @@ -53,7 +53,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: bawix/minio:2022 + image: docker.io/bawix/minio:2022 ports: - 9000:9000 - 9001:9001 diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 243aff3a3bc..a44a15f1eed 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -52,7 +52,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: bawix/minio:2022 + image: docker.io/bawix/minio:2022 ports: - 9000:9000 - 9001:9001 diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index e6b53bba28c..ded8874db56 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -64,7 +64,7 @@ jobs: options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 minio: - image: bawix/minio:2022 + image: docker.io/bawix/minio:2022 ports: - 9000:9000 - 9001:9001 From 69a4a6fc93e5ef93d10abf7b9c407930d9120dcf Mon Sep 17 00:00:00 2001 From: Machac Date: Tue, 25 Nov 2025 16:38:34 +0100 Subject: [PATCH 054/174] Release 6.5.0-RC.3 Integrate MinIO client setup in `FileFieldTest` to manage bucket creation. --- .../domain/dataset/FileFieldTest.groovy | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy b/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy index efbf77af576..42e170d2b4f 100644 --- a/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy @@ -5,6 +5,7 @@ import com.netgrif.application.engine.ApplicationEngine import com.netgrif.application.engine.TestHelper import com.netgrif.application.engine.auth.domain.IUser import com.netgrif.application.engine.auth.service.interfaces.IUserService +import com.netgrif.application.engine.files.minio.MinIoHostInfo import com.netgrif.application.engine.importer.service.Importer import com.netgrif.application.engine.petrinet.domain.PetriNet import com.netgrif.application.engine.petrinet.domain.VersionType @@ -14,6 +15,9 @@ import com.netgrif.application.engine.startup.SuperCreator import com.netgrif.application.engine.workflow.domain.Case import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest +import io.minio.BucketExistsArgs +import io.minio.MakeBucketArgs +import io.minio.MinioClient import org.assertj.core.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -56,6 +60,9 @@ class FileFieldTest { public static final String USER_EMAIL = "super@netgrif.com" public static final String MOCK_FILE_NAME = "hello.txt" + public static final String BUCKET = "default" + + static MinioClient mc; @Value('${admin.password:password}') private String userPassword @@ -91,6 +98,17 @@ class FileFieldTest { @BeforeEach void setup() { testHelper.truncateDbs() + + mc = MinioClient.builder() + .endpoint("http://127.0.0.1:9000") + .credentials("root", "password") + .build(); + + boolean exists = mc.bucketExists(BucketExistsArgs.builder().bucket(BUCKET).build()); + if (!exists) { + mc.makeBucket(MakeBucketArgs.builder().bucket(BUCKET).build()); + } + mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(SecurityMockMvcConfigurers.springSecurity()) From 705006cc6f8cb4efaf193dff434e92960f390b1e Mon Sep 17 00:00:00 2001 From: Machac Date: Tue, 25 Nov 2025 16:40:57 +0100 Subject: [PATCH 055/174] Release 6.5.0-RC.3 Integrate MinIO client setup in `FileListFieldTest` for bucket creation. --- .../domain/dataset/FileFieldTest.groovy | 1 - .../domain/dataset/FileListFieldTest.groovy | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy b/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy index 42e170d2b4f..d137fa6d47a 100644 --- a/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileFieldTest.groovy @@ -5,7 +5,6 @@ import com.netgrif.application.engine.ApplicationEngine import com.netgrif.application.engine.TestHelper import com.netgrif.application.engine.auth.domain.IUser import com.netgrif.application.engine.auth.service.interfaces.IUserService -import com.netgrif.application.engine.files.minio.MinIoHostInfo import com.netgrif.application.engine.importer.service.Importer import com.netgrif.application.engine.petrinet.domain.PetriNet import com.netgrif.application.engine.petrinet.domain.VersionType diff --git a/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy b/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy index 6cc02b28c37..422e7f4b9b8 100644 --- a/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FileListFieldTest.groovy @@ -14,6 +14,9 @@ import com.netgrif.application.engine.startup.SuperCreator import com.netgrif.application.engine.workflow.domain.Case import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService import com.netgrif.application.engine.workflow.web.requestbodies.file.FileFieldRequest +import io.minio.BucketExistsArgs +import io.minio.MakeBucketArgs +import io.minio.MinioClient import org.assertj.core.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -54,6 +57,9 @@ class FileListFieldTest { public static final String TASK_TITLE = "Task" public static final String USER_EMAIL = "super@netgrif.com" public static final String MOCK_FILE_NAME = "hello.txt" + public static final String BUCKET = "default" + + static MinioClient mc; @Value('${admin.password:password}') private String userPassword @@ -89,6 +95,17 @@ class FileListFieldTest { @BeforeEach void setup() { testHelper.truncateDbs() + + mc = MinioClient.builder() + .endpoint("http://127.0.0.1:9000") + .credentials("root", "password") + .build(); + + boolean exists = mc.bucketExists(BucketExistsArgs.builder().bucket(BUCKET).build()); + if (!exists) { + mc.makeBucket(MakeBucketArgs.builder().bucket(BUCKET).build()); + } + mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(SecurityMockMvcConfigurers.springSecurity()) From 792f567de017ecbc391d404cda282ddfe130da15 Mon Sep 17 00:00:00 2001 From: Machac Date: Mon, 2 Mar 2026 10:16:18 +0100 Subject: [PATCH 056/174] Release 6.5.0-RC.3 - update version elastic --- .github/workflows/master-build.yml | 2 +- .github/workflows/pr-build.yml | 2 +- .github/workflows/release-build.yml | 2 +- docker-compose.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/master-build.yml b/.github/workflows/master-build.yml index 1e9711a16ff..1be49359a52 100644 --- a/.github/workflows/master-build.yml +++ b/.github/workflows/master-build.yml @@ -46,7 +46,7 @@ jobs: - 6379:6379 elasticsearch: - image: elasticsearch:7.17.3 + image: elasticsearch:7.17.28 ports: - 9200:9200 - 9300:9300 diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index a44a15f1eed..318013ca7ea 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -45,7 +45,7 @@ jobs: - 6379:6379 elasticsearch: - image: elasticsearch:7.17.3 + image: elasticsearch:7.17.28 ports: - 9200:9200 - 9300:9300 diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index ded8874db56..85978a4daca 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -57,7 +57,7 @@ jobs: - 6379:6379 elasticsearch: - image: elasticsearch:7.17.3 + image: elasticsearch:7.17.28 ports: - 9200:9200 - 9300:9300 diff --git a/docker-compose.yml b/docker-compose.yml index 20ae8727115..b578689d92a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: memory: "512M" docker-elastic: - image: elasticsearch:7.17.4 + image: elasticsearch:7.17.28 environment: - cluster.name=elasticsearch - discovery.type=single-node From 2821cc27d782beace055f3b55ae41e7246fd3736 Mon Sep 17 00:00:00 2001 From: palajsamuel Date: Thu, 23 Apr 2026 13:30:53 +0200 Subject: [PATCH 057/174] [NAE-2390] Action API Improvements - implemented new action API method to ActionDelegate - added getOption method to MapOptionsField - tests for new functionality added to ActionDelegateTest - test xml NAE-2390_action_api_improvements.xml added --- .../domain/dataset/MapOptionsField.groovy | 4 + .../logic/action/ActionDelegate.groovy | 589 +++++++++++++++++- .../engine/action/ActionDelegateTest.groovy | 351 ++++++++++- .../NAE-2390_action_api_improvements.xml | 214 +++++++ 4 files changed, 1134 insertions(+), 24 deletions(-) create mode 100644 src/test/resources/petriNets/NAE-2390_action_api_improvements.xml diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/MapOptionsField.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/MapOptionsField.groovy index 1a26e8db413..f0acc8e978a 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/MapOptionsField.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/MapOptionsField.groovy @@ -40,4 +40,8 @@ abstract class MapOptionsField extends Field { boolean isDynamic() { return this.optionsExpression != null } + + T getOption(String key) { + return this.options.get(key) + } } diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 26791594b3e..69391895d78 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -54,6 +54,7 @@ import com.netgrif.application.engine.startup.FilterRunner import com.netgrif.application.engine.startup.ImportHelper import com.netgrif.application.engine.utils.FullPageRequest import com.netgrif.application.engine.workflow.domain.Case +import com.netgrif.application.engine.workflow.domain.DataField import com.netgrif.application.engine.workflow.domain.QCase import com.netgrif.application.engine.workflow.domain.QTask import com.netgrif.application.engine.workflow.domain.Task @@ -62,6 +63,8 @@ import com.netgrif.application.engine.workflow.domain.eventoutcomes.caseoutcomes import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.GetDataEventOutcome import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.SetDataEventOutcome import com.netgrif.application.engine.workflow.domain.eventoutcomes.taskoutcomes.AssignTaskEventOutcome +import com.netgrif.application.engine.workflow.domain.eventoutcomes.taskoutcomes.CancelTaskEventOutcome +import com.netgrif.application.engine.workflow.domain.eventoutcomes.taskoutcomes.FinishTaskEventOutcome import com.netgrif.application.engine.workflow.domain.eventoutcomes.taskoutcomes.TaskEventOutcome import com.netgrif.application.engine.workflow.service.FileFieldInputStream import com.netgrif.application.engine.workflow.service.TaskService @@ -915,11 +918,194 @@ class ActionDelegate { return result.content } + /** + * Finds cases referenced by a field in its value. + * + * Use this overload when working on a case from the current action context. For working with fields from out of the + * current action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns an empty list.

+ *

If the value cannot be converted to case IDs, this method returns {@code null}.

+ * + * @param caseRef field whose value contains case IDs, may be of types + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#CASE_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return list of matching cases, or an empty list when the field value is {@code null} + * @see ActionDelegate#findCases(DataField) + * @see ActionDelegate#findCases(List) + * @see ActionDelegate#findCases(Closure) + * @see ActionDelegate#findCases(Closure, Pageable) + */ + List findCases(Field caseRef) { + if(caseRef.value == null) { + log.error("Value of field with id [${caseRef.importId}] is null, returning empty list.") + return [] + } + try { + return this.findCases([caseRef.value].flatten() as List) + } catch (ClassCastException e) { + log.error("Method cannot be used with field with id [${caseRef.importId}].", e) + return null + } + } + + /** + * Finds cases referenced by a dataField in its value. + * + * Use this overload when working on a case not from the current action context. For working with fields from the current + * action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns an empty list.

+ *

If the value cannot be converted to case IDs, this method returns {@code null}.

+ * + * @param caseRef field whose value contains case IDs, may be of types + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#CASE_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return list of matching cases, or an empty list when the field value is {@code null} + * @see ActionDelegate#findCases(Field) + * @see ActionDelegate#findCases(List) + * @see ActionDelegate#findCases(Closure) + * @see ActionDelegate#findCases(Closure, Pageable) + */ + List findCases(DataField caseRef) { + if(caseRef.value == null) { + log.error("Value of field is null, returning empty list.") + return [] + } + try { + return this.findCases([caseRef.value].flatten() as List) + } catch (ClassCastException e) { + log.error("Method cannot be used with field.", e) + return null + } + } + + + /** + * Finds cases by their MongoDB IDs. + * + * @param mongoIds list of case IDs + * @return list of matching cases, or an empty list when the input is {@code null} + * @see ActionDelegate#findCases(Field) + * @see ActionDelegate#findCases(DataField) + * @see ActionDelegate#findCases(Closure) + * @see ActionDelegate#findCases(Closure, Pageable) + */ + List findCases(List mongoIds) { + if(mongoIds == null) { + log.warn("Null value detected, returning empty list.") + return [] + } + return workflowService.findAllById(mongoIds) + } + Case findCase(Closure predicate) { QCase qCase = new QCase("case") return workflowService.searchOne(predicate(qCase)) } + + + /** + * Finds the first case referenced by a field in its value. + * + * Use this overload when working on a case from current action context. For working with fields from out of the + * current action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns an empty list.

+ *

If the value cannot be converted to case IDs, this method returns {@code null}.

+ * + * @param caseRef field whose value contains case IDs, may be of types + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#CASE_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return list of matching cases, or an empty list when the field value is {@code null} + * @see ActionDelegate#findCase(DataField) + * @see ActionDelegate#findCase(String) + * @see ActionDelegate#findCase(Closure) + */ + Case findCase(Field caseRef) { + if(caseRef.value == null) { + log.error("Value of field with id [${caseRef.importId}] is null, returning null.") + return null + } + try { + List castValue = [caseRef.value].flatten() as List + if(castValue.size() == 0) { + log.error("Value of field with id [${caseRef.importId}] does not contain at least one element, returning null.") + return null + } + return this.findCase(castValue[0]) + } catch (ClassCastException e) { + log.error("Method cannot be used with field with id [${caseRef.importId}].", e) + return null + } + } + + + /** + * Finds the first case referenced by a dataField in its value. + * + * Use this overload when working on a case from out of current action context. For working with fields from the current + * action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns an empty list.

+ *

If the value cannot be converted to case IDs, this method returns {@code null}.

+ * + * @param caseRef field whose value contains case IDs, may be of types + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#CASE_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return list of matching cases, or an empty list when the field value is {@code null} + * @see ActionDelegate#findCase(Field) + * @see ActionDelegate#findCase(String) + * @see ActionDelegate#findCase(Closure) + */ + Case findCase(DataField caseRef) { + if(caseRef.value == null) { + log.error("Value of field is null, returning null.") + return null + } + try { + List castValue = [caseRef.value].flatten() as List + if(castValue.size() == 0) { + log.error("Value of field does not contain at least one element, returning null.") + return null + } + return this.findCase(castValue[0]) + } catch (ClassCastException e) { + log.error("Method cannot be used with field.", e) + return null + } + } + + /** + * Finds case by its MongoDB ID. + * + * @param mongoId case IDs + * @return resulting case + * @see ActionDelegate#findCase(Field) + * @see ActionDelegate#findCase(DataField) + * @see ActionDelegate#findCase(Closure) + */ + Case findCase(String mongoId) { + return workflowService.findOne(mongoId) + } + Case createCase(String identifier, String title = null, String color = "", IUser author = userService.loggedOrSystem, Locale locale = LocaleContextHolder.getLocale(), Map params = [:]) { return workflowService.createCaseByIdentifier(identifier, title, color, author.transformToLoggedUser(), locale, params).getCase() } @@ -930,19 +1116,70 @@ class ActionDelegate { return outcome.getCase() } + /** + * Deletes a case by its MongoDB ID. + * + * @param mongoId case identifier + * @return deleted case, or {@code null} when the input is {@code null} + */ + Case deleteCase(String mongoId) { + if(mongoId == null){ + log.warn("Null value detected, returning null.") + return null + } + return this.deleteCase(workflowService.findOne(mongoId)) + } + + /** + * Deletes the provided case. + * + * @param toDelete case to delete + * @return deleted case, or {@code null} when the input is {@code null} + */ + Case deleteCase(Case toDelete) { + if(toDelete == null){ + log.warn("Null value detected, returning null.") + return null + } + return workflowService.deleteCase(toDelete).case + } + Task assignTask(String transitionId, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { String taskId = getTaskId(transitionId, aCase) - AssignTaskEventOutcome outcome = taskService.assignTask(user.transformToLoggedUser(), taskId, params) - this.outcomes.add(outcome) - return outcome.getTask() + return addTaskOutcomeAndReturnTask(taskService.assignTask(user.transformToLoggedUser(), taskId, params)) } Task assignTask(Task task, IUser user = userService.loggedOrSystem, Map params = [:]) { return addTaskOutcomeAndReturnTask(taskService.assignTask(task, user, params)) } - void assignTasks(List tasks, IUser assignee = userService.loggedOrSystem, Map params = [:]) { - this.outcomes.addAll(taskService.assignTasks(tasks, assignee, params)) + /** + * Assigns tasks for all transitions in the provided list and returns the assigned tasks. + * + * @param transitionIds transition identifiers whose tasks should be assigned + * @param aCase case used to resolve the tasks, defaults to the current case + * @param user user to assign the tasks to, defaults to the logged or system user + * @param params additional parameters + * @return assigned tasks + */ + List assignTasksByTransitions(List transitionIds, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { + List taskIds = getTaskIds(transitionIds, aCase) + List tasks = taskService.findAllById(taskIds) + return assignTasks(tasks, user, params) + } + + /** + * Assigns the provided tasks and returns the assigned tasks. + * + * @param tasks tasks to assign + * @param assignee user to assign the tasks to, defaults to the logged or system user + * @param params additional parameters + * @return assigned tasks + */ + List assignTasks(List tasks, IUser assignee = userService.loggedOrSystem, Map params = [:]) { + List outcomes = taskService.assignTasks(tasks, assignee, params) + this.outcomes.addAll(outcomes) + return outcomes.collect { it.task } } Task cancelTask(String transitionId, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { @@ -954,8 +1191,34 @@ class ActionDelegate { return addTaskOutcomeAndReturnTask(taskService.cancelTask(task, user, params)) } - void cancelTasks(List tasks, IUser user = userService.loggedOrSystem, Map params = [:]) { - this.outcomes.addAll(taskService.cancelTasks(tasks, user, params)) + + /** + * Cancels tasks for all transitions in the provided list and returns the canceled tasks. + * + * @param transitionIds transition identifiers whose tasks should be canceled + * @param aCase case used to resolve the tasks, defaults to the current case + * @param user user performing the cancellation, defaults to the logged or system user + * @param params additional parameters + * @return canceled tasks + */ + List cancelTasksByTransitions(List transitionIds, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { + List taskIds = getTaskIds(transitionIds, aCase) + List tasks = taskService.findAllById(taskIds) + return cancelTasks(tasks, user, params) + } + + /** + * Cancels the provided tasks and returns the canceled tasks. + * + * @param tasks tasks to cancel + * @param user user performing the cancellation, defaults to the logged or system user + * @param params additional parameters + * @return canceled tasks + */ + List cancelTasks(List tasks, IUser user = userService.loggedOrSystem, Map params = [:]) { + List outcomes = taskService.cancelTasks(tasks, user, params) + this.outcomes.addAll(outcomes) + return outcomes.collect { it.task } } private Task addTaskOutcomeAndReturnTask(TaskEventOutcome outcome) { @@ -963,17 +1226,42 @@ class ActionDelegate { return outcome.getTask() } - void finishTask(String transitionId, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { + Task finishTask(String transitionId, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { String taskId = getTaskId(transitionId, aCase) - addTaskOutcomeAndReturnTask(taskService.finishTask(user.transformToLoggedUser(), taskId, params)) + return addTaskOutcomeAndReturnTask(taskService.finishTask(user.transformToLoggedUser(), taskId, params)) + } + + Task finishTask(Task task, IUser user = userService.loggedOrSystem, Map params = [:]) { + return addTaskOutcomeAndReturnTask(taskService.finishTask(task, user, params)) } - void finishTask(Task task, IUser user = userService.loggedOrSystem, Map params = [:]) { - addTaskOutcomeAndReturnTask(taskService.finishTask(task, user, params)) + /** + * Finishes tasks for all transitions in the provided list and returns the finished tasks. + * + * @param transitionIds transition identifiers whose tasks should be finished + * @param aCase case used to resolve the tasks, defaults to the current case + * @param user user performing the finish operation, defaults to the logged or system user + * @param params additional parameters + * @return finished tasks + */ + List finishTasksByTransitions(List transitionIds, Case aCase = useCase, IUser user = userService.loggedOrSystem, Map params = [:]) { + List taskIds = getTaskIds(transitionIds, aCase) + List tasks = taskService.findAllById(taskIds) + return finishTasks(tasks, user, params) } - void finishTasks(List tasks, IUser finisher = userService.loggedOrSystem, Map params = [:]) { - this.outcomes.addAll(taskService.finishTasks(tasks, finisher, params)) + /** + * Finishes the provided tasks and returns the finished tasks. + * + * @param tasks tasks to finish + * @param finisher user performing the finish operation, defaults to the logged or system user + * @param params additional parameters + * @return finished tasks + */ + List finishTasks(List tasks, IUser finisher = userService.loggedOrSystem, Map params = [:]) { + List outcomes = taskService.finishTasks(tasks, finisher) + this.outcomes.addAll(outcomes) + return outcomes.collect { it.task } } List findTasks(Closure predicate) { @@ -988,13 +1276,262 @@ class ActionDelegate { return result.content } + /** + * Finds tasks referenced by a field in its value. + * + * Use this overload when working on a case from current action context. For working with fields from out of the + * current action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns an empty list.

+ *

If the value cannot be converted to task IDs, this method returns {@code null}.

+ * + * @param taskRef field whose value contains task IDs + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TASK_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return list of matching tasks, or an empty list when the field value is {@code null} + * @see ActionDelegate#findTasks(DataField) + * @see ActionDelegate#findTasks(List) + */ + List findTasks(Field taskRef) { + if(taskRef.value == null) { + log.error("Value of field with id [${taskRef.importId}] is null, returning empty list.") + return [] + } + try { + return this.findTasks([taskRef.value].flatten() as List) + } catch (ClassCastException e) { + log.error("Method cannot be used with field with id [${taskRef.importId}].", e) + return null + } + } + + /** + * Finds tasks referenced by a dataField in its value. + * + * Use this overload when working on a case not from the current action context. For working with fields from out of the + * current action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns an empty list.

+ *

If the value cannot be converted to task IDs, this method returns {@code null}.

+ * + * @param taskRef field whose value contains task IDs + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TASK_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return list of matching tasks, or an empty list when the field value is {@code null} + * @see ActionDelegate#findTasks(Field) + * @see ActionDelegate#findTasks(List) + */ + List findTasks(DataField taskRef) { + if(taskRef.value == null) { + log.error("Value of field is null, returning empty list.") + return [] + } + try { + return this.findTasks([taskRef.value].flatten() as List) + } catch (ClassCastException e) { + log.error("Method cannot be used with field.", e) + return null + } + } + + /** + * Finds tasks by their MongoDB IDs. + * + * @param mongoIds task identifiers + * @return list of matching tasks, or an empty list when the input is {@code null} + * @see ActionDelegate#findTasks(Field) + * @see ActionDelegate#findTasks(DataField) + */ + List findTasks(List mongoIds) { + if(mongoIds == null) { + log.warn("Null value detected, returning empty list.") + return [] + } + return taskService.findAllById(mongoIds) + } + Task findTask(Closure predicate) { QTask qTask = new QTask("task") return taskService.searchOne(predicate(qTask)) } Task findTask(String mongoId) { - return taskService.searchOne(QTask.task._id.eq(new ObjectId(mongoId))) + return taskService.findOne(mongoId) + } + + /** + * Finds the first task referenced by a field in its value. + * + * Use this overload when working on a case from the current action context. For working with fields from out of the + * current action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns {@code null}.

+ *

If the field contains no value or the value cannot be converted to a task ID, this method returns + * {@code null}.

+ * + * @param taskRef field whose value contains a task ID + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TASK_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return referenced task, or {@code null} when the field value is invalid + * @see ActionDelegate#findTask(DataField) + * @see ActionDelegate#findTask(String) + * @see ActionDelegate#findTask(Closure) + */ + Task findTask(Field taskRef) { + if(taskRef.value == null) { + log.error("Value of field with id [${taskRef.importId}] is null, returning null") + return null + } + try { + List castValue = [taskRef.value].flatten() as List + if(castValue.size() == 0) { + log.error("Value of field with id [${taskRef.importId}] does not contain at least one element, returning null.") + return null + } + return this.findTask(castValue[0]) + } catch (ClassCastException e) { + log.error("Method cannot be used with field with id [${taskRef.importId}].", e) + return null + } + } + + /** + * Finds the first task referenced by a dataField in its value. + * + * Use this overload when working on a case not from the current action context. For working with fields from out of the + * current action context see other overloads of this action. + * + *

If the field value is {@code null}, this method returns {@code null}.

+ *

If the field contains no value or the value cannot be converted to a task ID, this method returns + * {@code null}.

+ * + * @param taskRef field whose value contains a task ID + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TASK_REF}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#MULTICHOICE_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, + * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, + * @return referenced task, or {@code null} when the field value is invalid + * @see ActionDelegate#findTask(Field) + * @see ActionDelegate#findTask(String) + * @see ActionDelegate#findTask(Closure) + */ + Task findTask(DataField taskRef) { + if(taskRef.value == null) { + log.error("Value of field is null, returning null") + return null + } + try { + List castValue = [taskRef.value].flatten() as List + if(castValue.size() == 0) { + log.error("Value of field does not contain at least one element, returning null.") + return null + } + return taskService.findOne(castValue[0]) + } catch (ClassCastException e) { + log.error("Method cannot be used with field.", e) + return null + } + } + + /** + * Finds a Petri net by its MongoDB ID. + * + * @param mongoId Petri net identifier + * @return matching Petri net, or {@code null} when the input is {@code null} + */ + PetriNet findPetriNet(String mongoId) { + if(mongoId == null){ + log.warn("Null value detected, returning null.") + return null + } + return petriNetService.getPetriNet(mongoId) + } + + /** + * Finds a Petri net by its {@link ObjectId}. + * + * @param objectId Petri net object identifier + * @return matching Petri net, or {@code null} when the input is {@code null} + */ + PetriNet findPetriNet(ObjectId objectId) { + if(objectId == null){ + log.warn("Null value detected, returning null.") + return null + } + return petriNetService.get(objectId) + } + + /** + * Finds Petri nets by their MongoDB IDs. + * + * @param mongoIds list of Petri net identifiers + * @return matching Petri nets, or an empty list when the input is {@code null} + */ + List findPetriNets(List mongoIds) { + if(mongoIds == null){ + log.warn("Null value detected, returning empty list.") + return [] + } + return petriNetService.findAllById(mongoIds) + } + + /** + * Finds Petri nets by their {@link ObjectId} values. + * + * @param objectIds list of Petri net object identifiers + * @return matching Petri nets, or an empty list when the input is {@code null} + */ + List findPetriNetsByObjectIds(List objectIds) { + if(objectIds == null){ + log.warn("Null value detected, returning empty list.") + return [] + } + return petriNetService.get(objectIds as Collection) + } + + /** + * Finds a Petri net by its identifier and optional version. + * + * If the version is not provided, the newest available version is returned. + * + * @param identifier Petri net identifier + * @param version requested version, or {@code null} for the newest version + * @return matching Petri net, or {@code null} when the identifier is {@code null} + */ + PetriNet findPetriNetByIdentifier(String identifier, Version version = null) { + if(identifier == null) { + log.warn("Null identifier value detected, returning null.") + return null + } + return version == null ? petriNetService.getNewestVersionByIdentifier(identifier) : petriNetService.getPetriNet(identifier, version) + } + + /** + * Converts cases to a map of option keys and translated option values. + * + * @param casesToTransform cases to convert + * @param valueTransformation transformation used to derive the option label from a case, case title is used if not specified otherwise + * @param keyTransformation transformation used to derive the option key from a case, case stringId is used if not specified otherwise + * @return map of option keys and translated values + */ + Map casesToOptions(List casesToTransform, Closure valueTransformation = { return it.title }, Closure keyTransformation = { return it.stringId }) { + return casesToTransform.collectEntries { + [(keyTransformation(it)): new I18nString(valueTransformation(it))] + } } String getTaskId(String transitionId, Case aCase = useCase) { @@ -1002,6 +1539,18 @@ class ActionDelegate { refs.find { it.transitionId == transitionId }.stringId } + /** + * Returns task identifiers for tasks belonging to the provided transitions in the given case. + * + * @param transitionIds transition identifiers + * @param aCase case whose tasks should be inspected, defaults to the current case + * @return list of matching task identifiers + */ + List getTaskIds(List transitionIds, Case aCase = useCase) { + List refs = taskService.findAllByCase(aCase.stringId, null) + return refs.findAll { transitionIds.contains(it.transitionId) }.collect { it.stringId} + } + IUser assignRole(String roleMongoId, IUser user = userService.loggedUser) { IUser actualUser = userService.addRole(user, roleMongoId) return actualUser @@ -1526,8 +2075,8 @@ class ActionDelegate { } /** - * Action API case search function using Elasticsearch database - * @param requests the CaseSearchRequest list + * Action API task search function using Elasticsearch database + * @param requests the @link{ElasticTaskSearchRequest} list * @param loggedUser the user who is searching for the requests * @param page the order of page to return. by default it returns the first page * @param pageable the page configuration that will contain the requests @@ -1535,13 +2084,13 @@ class ActionDelegate { * @param isIntersection to decide null query handling * @return page of cases * */ - Page findTasks(List requests, LoggedUser loggedUser = userService.loggedOrSystem.transformToLoggedUser(), + Page findTasksElastic(List requests, LoggedUser loggedUser = userService.loggedOrSystem.transformToLoggedUser(), int page = 1, int pageSize = 25, Locale locale = Locale.default, boolean isIntersection = false) { return elasticTaskService.search(requests, loggedUser, PageRequest.of(page, pageSize), locale, isIntersection) } /** - * Action API case search function using Elasticsearch database + * Action API task search function using Elasticsearch database * @param request case search request * @param loggedUser the user who is searching for the requests * @param page the order of page to return. by default it returns the first page @@ -1550,10 +2099,10 @@ class ActionDelegate { * @param isIntersection to decide null query handling * @return page of cases * */ - Page findTasks(Map request, LoggedUser loggedUser = userService.loggedOrSystem.transformToLoggedUser(), + Page findTasksElastic(Map request, LoggedUser loggedUser = userService.loggedOrSystem.transformToLoggedUser(), int page = 1, int pageSize = 25, Locale locale = Locale.default, boolean isIntersection = false) { List requests = Collections.singletonList(new ElasticTaskSearchRequest(request)) - return findTasks(requests, loggedUser, page, pageSize, locale, isIntersection) + return findTasksElastic(requests, loggedUser, page, pageSize, locale, isIntersection) } List findDefaultFilters() { diff --git a/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy index 4900ac8595b..1f7240f5a53 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy @@ -7,9 +7,20 @@ import com.netgrif.application.engine.auth.domain.IUser import com.netgrif.application.engine.auth.service.interfaces.IUserService import com.netgrif.application.engine.auth.web.requestbodies.NewUserRequest import com.netgrif.application.engine.configuration.PublicViewProperties +import com.netgrif.application.engine.petrinet.domain.I18nString +import com.netgrif.application.engine.petrinet.domain.PetriNet +import com.netgrif.application.engine.petrinet.domain.VersionType +import com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField +import com.netgrif.application.engine.petrinet.domain.dataset.Field import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate +import com.netgrif.application.engine.petrinet.domain.version.Version +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService +import com.netgrif.application.engine.workflow.domain.Case +import com.netgrif.application.engine.workflow.domain.DataField +import com.netgrif.application.engine.workflow.domain.Task import com.netgrif.application.engine.workflow.service.interfaces.IFilterImportExportService import com.netgrif.application.engine.workflow.web.responsebodies.MessageResource +import org.bson.types.ObjectId import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -22,6 +33,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension import javax.mail.internet.MimeMessage import static java.util.Base64.* +import static org.junit.jupiter.api.Assertions.assertThrows +import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest @ActiveProfiles(["test"]) @@ -43,21 +56,27 @@ class ActionDelegateTest { @Autowired private PublicViewProperties publicViewProperties + @Autowired + private IPetriNetService petriNetService + + private static final ACTION_API_NET_IDENTIFIER = "action_api_improvements" + @BeforeEach void before() { testHelper.truncateDbs() + actionDelegate.outcomes = [] } @Test @Disabled("Context user") - void importFiltersTest(){ + void importFiltersTest() { List actionDelegateList = actionDelegate.importFilters() List importedTasksIds = importExportService.importFilters() assert actionDelegateList.size() == importedTasksIds.size() } @Test - void inviteUser(){ + void inviteUser() { GreenMail smtpServer = new GreenMail(new ServerSetup(2525, null, "smtp")) smtpServer.start() @@ -70,7 +89,7 @@ class ActionDelegateTest { } @Test - void deleteUser(){ + void deleteUser() { GreenMail smtpServer = new GreenMail(new ServerSetup(2525, null, "smtp")) smtpServer.start() String mail = "test@netgrif.com"; @@ -88,7 +107,7 @@ class ActionDelegateTest { @Test - void inviteUserNewUserRequest(){ + void inviteUserNewUserRequest() { GreenMail smtpServer = new GreenMail(new ServerSetup(2525, null, "smtp")) smtpServer.start() @@ -113,4 +132,328 @@ class ActionDelegateTest { assert actionDelegate.makeUrl(publicViewProperties.url, identifier) == url assert actionDelegate.makeUrl("test.netgrif.com/public", "identifier") == "test.netgrif.com/public/${getEncoder().encodeToString(identifier.bytes)}" } + + @Test + void testTaskActions() { + importTestPetriNet() + Case testCase = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + List taskIds = testCase.tasks.collect { it.task } + List tasks = actionDelegate.findTasks(taskIds) + + assert tasks != null + assert !tasks.empty + assert tasks.size() == taskIds.size() + + testCase = actionDelegate.setData("t1", testCase, [ + "enumeration_map" : [ + "type" : "enumeration_map", + "value": taskIds[0] + ], + "multichoice_map" : [ + "type" : "multichoice_map", + "value": taskIds + ], + "stringCollection": [ + "type" : "stringCollection", + "value": taskIds + ], + "taskRef" : [ + "type" : "taskRef", + "value": taskIds + ], + "text" : [ + "type" : "text", + "value": taskIds[0] + ] + ]).case + actionDelegate.useCase = testCase + +// fields with single value + ["enumeration_map", "text"].forEach { + assertTaskSearchResults(it, testCase, 1, true) + } + +// fields with collection value + ["multichoice_map", "stringCollection", "taskRef"].forEach { + assertTaskSearchResults(it, testCase, taskIds.size(), false) + } + } + + @Test + void testCaseActions() { + importTestPetriNet() + Case testCase1 = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + Case testCase2 = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + Case testCase3 = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + Case testCase4 = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + + List caseIds = [testCase1.stringId, testCase2.stringId, testCase3.stringId, testCase4.stringId] + + Case searchedCase = actionDelegate.findCase(caseIds[0]) + assert searchedCase != null + assert searchedCase.stringId == testCase1.stringId + + List searchedCases = actionDelegate.findCases(caseIds) + assert searchedCases != null + assert !searchedCases.empty + assert searchedCases.size() == caseIds.size() + + + testCase1 = actionDelegate.setData("t1", testCase1, [ + "enumeration_map" : [ + "type" : "enumeration_map", + "value": caseIds[0] + ], + "multichoice_map" : [ + "type" : "multichoice_map", + "value": caseIds + ], + "stringCollection": [ + "type" : "stringCollection", + "value": caseIds + ], + "caseRef" : [ + "type" : "caseRef", + "value": caseIds + ], + "text" : [ + "type" : "text", + "value": caseIds[0] + ] + ]).case + + actionDelegate.useCase = testCase1 +// fields with single value + ["enumeration_map", "text"].forEach { + assertCaseSearchResults(it, testCase1, 1, true) + } + +// fields with collection value + ["multichoice_map", "stringCollection", "caseRef"].forEach { + assertCaseSearchResults(it, testCase1, caseIds.size(), false) + } + } + + @Test + void testCaseDeletionActions() { + importTestPetriNet() + Case testCase1 = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + Case testCase2 = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + + testCase1 = actionDelegate.deleteCase(testCase1) + assertCaseDeletion(testCase1.stringId) + + testCase2 = actionDelegate.deleteCase(testCase2.stringId) + assertCaseDeletion(testCase2.stringId) + } + + @Test + void testPetriNetActions() { + PetriNet importedNet = importTestPetriNet() + String mongoId = importedNet.stringId + String netIdentifier = importedNet.getIdentifier() + ObjectId objectId = importedNet.objectId + + PetriNet foundNet = actionDelegate.findPetriNet(mongoId) + assert foundNet != null + assert foundNet.objectId == objectId + assert foundNet.identifier == netIdentifier + foundNet = null + + foundNet = actionDelegate.findPetriNet(objectId) + assert foundNet != null + assert foundNet.stringId == mongoId + assert foundNet.identifier == netIdentifier + foundNet = null + + PetriNet filterNet = petriNetService.getByIdentifier("filter")[0] + String mongoId2 = filterNet.stringId + String netIdentifier2 = filterNet.getIdentifier() + ObjectId objectId3 = filterNet.objectId + + def searchTargets = [mongoId, mongoId2] + List searchResults = actionDelegate.findPetriNets(searchTargets as List) + assert searchResults != null + assert !searchResults.empty + assert searchResults.collect { it.stringId }.containsAll(searchTargets) + searchResults = null + + searchTargets = [objectId, objectId] + searchResults = actionDelegate.findPetriNetsByObjectIds(searchTargets as List) + assert searchResults != null + assert !searchResults.empty + assert searchResults.collect { it.objectId }.containsAll(searchTargets) + + PetriNet importedNet2 = importTestPetriNet() + assert importedNet2 != null + assert importedNet2.identifier == netIdentifier + assert importedNet2.stringId != mongoId + assert importedNet2.version != importedNet.version + + foundNet = actionDelegate.findPetriNetByIdentifier(importedNet2.identifier, new Version(1, 0, 0)) + assert foundNet != null + assert foundNet.identifier == netIdentifier + assert foundNet.stringId == mongoId + assert foundNet.version == importedNet.version + assert foundNet.version != importedNet2.version + foundNet = null + +// find newest + foundNet = actionDelegate.findPetriNetByIdentifier(importedNet2.identifier) + assert foundNet != null + assert foundNet.identifier == netIdentifier + assert foundNet.stringId != mongoId + assert foundNet.version != importedNet.version + assert foundNet.version == importedNet2.version + } + + @Test + void testOptionsActions() { + PetriNet net = importTestPetriNet() + Case case1 = actionDelegate.createCase(net, "Test title 1") + Case case2 = actionDelegate.createCase(net, "Test title 2") + + List cases = [case1, case2] + Map options = actionDelegate.casesToOptions(cases) + assert options != null + assert options.size() == cases.size() + assert options.keySet().containsAll(cases.collect { it.stringId }) + assert options.get(case1.stringId).defaultValue == case1.title + assert options.get(case2.stringId).defaultValue == case2.title + options = null + + String keyTransformationTestString = "Key transformation test " + String valueTransformationTestString = "Value transformation test " + options = actionDelegate.casesToOptions(cases, { return "Value transformation test ".concat(it.title) }, { return "Key transformation test ".concat(it.stringId) }) + assert options != null + assert options.size() == cases.size() + assert options.keySet().containsAll(cases.collect { keyTransformationTestString.concat(it.stringId) }) + assert options.get(keyTransformationTestString.concat(case1.stringId)).defaultValue == valueTransformationTestString.concat(case1.title) + assert options.get(keyTransformationTestString.concat(case2.stringId)).defaultValue == valueTransformationTestString.concat(case2.title) + + case1.dataSet["enumeration_map"].options = options + actionDelegate.useCase = case1 + actionDelegate.initFieldsMap(["enumeration_map": "enumeration_map"]) + EnumerationMapField field = (EnumerationMapField) actionDelegate.map.get("enumeration_map") + assert field != null + assert field.options != null + I18nString option = field.getOption(keyTransformationTestString.concat(case1.stringId)) + assert option != null + assert option.defaultValue == valueTransformationTestString.concat(case1.title) + } + + @Test + void testTaskEventActions() { + importTestPetriNet() + Case testCase = actionDelegate.createCase(ACTION_API_NET_IDENTIFIER) + List transitionIds = ["t1", "t2"] + IUser user = userService.getLoggedOrSystem() + +// testing "byTransition" variants should be enough, as they call methods, that tak List instead of List + List tasks = actionDelegate.assignTasksByTransitions(transitionIds, testCase) + assert tasks != null + assert tasks.size() == transitionIds.size() + assert tasks.stream().allMatch { it.user.email == user.email } + assert tasks.stream().allMatch { it.userId == user.stringId } + assert tasks.stream().allMatch { it.startDate != null } + tasks = null + + tasks = actionDelegate.cancelTasksByTransitions(transitionIds, testCase) + assert tasks != null + assert tasks.size() == transitionIds.size() + assert tasks.stream().allMatch { it.userId == null } + assert tasks.stream().allMatch { it.startDate == null } + tasks = null + + actionDelegate.assignTasksByTransitions(transitionIds, testCase) + tasks = actionDelegate.finishTasksByTransitions(transitionIds, testCase) + assert tasks != null + assert tasks.size() == transitionIds.size() + assert tasks.stream().allMatch { it.finishedBy == user.stringId } + assert tasks.stream().allMatch { it.finishDate != null } + assert tasks.stream().allMatch { it.userId == null } + tasks = actionDelegate.findTasks(tasks.collect { it.stringId }) + assert tasks.size() == 1 + assert tasks[0].transitionId == "t2" + } + + private void assertCaseDeletion(String deletedCaseId) { + Exception e = assertThrows(IllegalArgumentException.class, () -> { + actionDelegate.findCase(deletedCaseId) + }) + + String expectedMessage = "Could not find Case with id [${deletedCaseId}]" + assertTrue(expectedMessage == e.getMessage()) + } + + private void assertTaskSearchResults(String fieldId, Case testCase, int sizeToCheck, boolean singleValueField) { + actionDelegate.initFieldsMap([(fieldId): fieldId]) + Field field = actionDelegate.map.get(fieldId) + String firstTaskId = ([field.value].flatten() as List)[0] + + Task task = actionDelegate.findTask(field) + assert fieldId && task != null + assert fieldId && task.stringId == firstTaskId + task = null + + List tasks = actionDelegate.findTasks(field) + assert fieldId && tasks != null + assert fieldId && !tasks.empty + assert fieldId && tasks.size() == sizeToCheck + if (singleValueField) { + assert fieldId && tasks[0].stringId == firstTaskId + } + tasks = null + + DataField dataField = testCase.getDataField(fieldId) + task = actionDelegate.findTask(dataField) + assert fieldId && task != null + assert fieldId && task.stringId == firstTaskId + + tasks = actionDelegate.findTasks(dataField) + assert fieldId && tasks != null + assert fieldId && !tasks.empty + assert fieldId && tasks.size() == sizeToCheck + if (singleValueField) { + assert fieldId && tasks[0].stringId == firstTaskId + } + } + + private void assertCaseSearchResults(String fieldId, Case testCase, int sizeToCheck, boolean singleValueField) { + actionDelegate.initFieldsMap([(fieldId): fieldId]) + Field field = actionDelegate.map.get(fieldId) + String firstCaseId = ([field.value].flatten() as List)[0] + + Case searchedCase = actionDelegate.findCase(field) + assert fieldId && searchedCase != null + assert fieldId && searchedCase.stringId == firstCaseId + searchedCase = null + + List searchedCases = actionDelegate.findCases(field) + assert fieldId && searchedCases != null + assert fieldId && !searchedCases.empty + assert fieldId && searchedCases.size() == sizeToCheck + if (singleValueField) { + assert fieldId && searchedCases[0].stringId == firstCaseId + } + searchedCases = null + + + DataField dataField = testCase.getDataField(fieldId) + searchedCase = actionDelegate.findCase(dataField) + assert fieldId && searchedCase != null + assert fieldId && searchedCase.stringId == firstCaseId + + searchedCases = actionDelegate.findCases(dataField) + assert fieldId && searchedCases != null + assert fieldId && !searchedCases.empty + assert fieldId && searchedCases.size() == sizeToCheck + if (singleValueField) { + assert fieldId && searchedCases[0].stringId == firstCaseId + } + } + + private PetriNet importTestPetriNet() { + return petriNetService.importPetriNet(new FileInputStream("src/test/resources/petriNets/NAE-2390_action_api_improvements.xml"), VersionType.MAJOR, userService.getLoggedOrSystem().transformToLoggedUser()).getNet() + } } diff --git a/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml b/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml new file mode 100644 index 00000000000..6c201f9ea07 --- /dev/null +++ b/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml @@ -0,0 +1,214 @@ + + action_api_improvements + 1.0.0 + NEW + New Model + device_hub + true + true + false + + caseRef + + <allowedNets> + <allowedNet>action_api_improvements</allowedNet> + </allowedNets> + </data> + <data type="enumeration"> + <id>enumeration</id> + <title/> + </data> + <data type="enumeration_map"> + <id>enumeration_map</id> + <title/> + </data> + <data type="multichoice"> + <id>multichoice</id> + <title/> + </data> + <data type="multichoice_map"> + <id>multichoice_map</id> + <title/> + </data> + <data type="stringCollection"> + <id>stringCollection</id> + <title/> + </data> + <data type="taskRef"> + <id>taskRef</id> + <title/> + </data> + <data type="text"> + <id>text</id> + <title/> + </data> + <transition> + <id>t1</id> + <x>336</x> + <y>112</y> + <label/> + <dataGroup> + <id>t1_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>enumeration_map</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>caseRef</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>taskRef</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>stringCollection</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>2</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>enumeration</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>2</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>multichoice</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>3</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>text</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>3</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>multichoice_map</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>4</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + </dataGroup> + </transition> + <transition> + <id>t2</id> + <x>208</x> + <y>240</y> + <label/> + </transition> + <transition> + <id>t3</id> + <x>336</x> + <y>240</y> + <label/> + </transition> + <transition> + <id>t4</id> + <x>464</x> + <y>240</y> + <label/> + </transition> + <place> + <id>p1</id> + <x>208</x> + <y>112</y> + <tokens>1</tokens> + <static>false</static> + </place> + <place> + <id>p2</id> + <x>464</x> + <y>112</y> + <tokens>0</tokens> + <static>false</static> + </place> + <arc> + <id>a1</id> + <type>regular</type> + <sourceId>p1</sourceId> + <destinationId>t1</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a2</id> + <type>regular</type> + <sourceId>t1</sourceId> + <destinationId>p2</destinationId> + <multiplicity>1</multiplicity> + </arc> +</document> \ No newline at end of file From a1986acaea8be783ee191ce1aeac91cadab9d081 Mon Sep 17 00:00:00 2001 From: palajsamuel <palaj@netgrif.com> Date: Thu, 23 Apr 2026 14:33:30 +0200 Subject: [PATCH 058/174] [NAE-2390] Action API Improvements - implemented new action API method to ActionDelegate - added getOption method to MapOptionsField - tests for new functionality added to ActionDelegateTest - test xml NAE-2390_action_api_improvements.xml added --- .../logic/action/ActionDelegate.groovy | 30 ++++----- .../engine/action/ActionDelegateTest.groovy | 67 +++++++++++-------- .../NAE-2390_action_api_improvements.xml | 2 +- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 69391895d78..6d401e02f80 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -984,7 +984,7 @@ class ActionDelegate { return this.findCases([caseRef.value].flatten() as List<String>) } catch (ClassCastException e) { log.error("Method cannot be used with field.", e) - return null + return [] } } @@ -993,14 +993,14 @@ class ActionDelegate { * Finds cases by their MongoDB IDs. * * @param mongoIds list of case IDs - * @return list of matching cases, or an empty list when the input is {@code null} + * @return list of matching cases, or an empty list when the input is {@code null} or {@code empty} * @see ActionDelegate#findCases(Field) * @see ActionDelegate#findCases(DataField) * @see ActionDelegate#findCases(Closure) * @see ActionDelegate#findCases(Closure, Pageable) */ List<Case> findCases(List<String> mongoIds) { - if(mongoIds == null) { + if(mongoIds == null || mongoIds.empty) { log.warn("Null value detected, returning empty list.") return [] } @@ -1020,7 +1020,7 @@ class ActionDelegate { * Use this overload when working on a case from current action context. For working with fields from out of the * current action context see other overloads of this action. * - * <p>If the field value is {@code null}, this method returns an empty list.</p> + * <p>If the field value is {@code null}, this method returns {@code null}.</p> * <p>If the value cannot be converted to case IDs, this method returns {@code null}.</p> * * @param caseRef field whose value contains case IDs, may be of types @@ -1030,7 +1030,7 @@ class ActionDelegate { * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, - * @return list of matching cases, or an empty list when the field value is {@code null} + * @return referenced case, or {@code null} when the field value is invalid * @see ActionDelegate#findCase(DataField) * @see ActionDelegate#findCase(String) * @see ActionDelegate#findCase(Closure) @@ -1060,7 +1060,7 @@ class ActionDelegate { * Use this overload when working on a case from out of current action context. For working with fields from the current * action context see other overloads of this action. * - * <p>If the field value is {@code null}, this method returns an empty list.</p> + * <p>If the field value is {@code null}, this method returns {@code null}.</p> * <p>If the value cannot be converted to case IDs, this method returns {@code null}.</p> * * @param caseRef field whose value contains case IDs, may be of types @@ -1070,7 +1070,7 @@ class ActionDelegate { * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#ENUMERATION_MAP}, * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#STRING_COLLECTION}, * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TEXT}, - * @return list of matching cases, or an empty list when the field value is {@code null} + * @return referenced case, or {@code null} when the dataField value is invalid * @see ActionDelegate#findCase(Field) * @see ActionDelegate#findCase(String) * @see ActionDelegate#findCase(Closure) @@ -1259,7 +1259,7 @@ class ActionDelegate { * @return finished tasks */ List<Task> finishTasks(List<Task> tasks, IUser finisher = userService.loggedOrSystem, Map<String, String> params = [:]) { - List<FinishTaskEventOutcome> outcomes = taskService.finishTasks(tasks, finisher) + List<FinishTaskEventOutcome> outcomes = taskService.finishTasks(tasks, finisher, params) this.outcomes.addAll(outcomes) return outcomes.collect { it.task } } @@ -1346,12 +1346,12 @@ class ActionDelegate { * Finds tasks by their MongoDB IDs. * * @param mongoIds task identifiers - * @return list of matching tasks, or an empty list when the input is {@code null} + * @return list of matching tasks, or an empty list when the input is {@code null} org {@code empty} * @see ActionDelegate#findTasks(Field) * @see ActionDelegate#findTasks(DataField) */ List<Task> findTasks(List<String> mongoIds) { - if(mongoIds == null) { + if(mongoIds == null || mongoIds.empty) { log.warn("Null value detected, returning empty list.") return [] } @@ -1440,7 +1440,7 @@ class ActionDelegate { log.error("Value of field does not contain at least one element, returning null.") return null } - return taskService.findOne(castValue[0]) + return this.findTask(castValue[0]) } catch (ClassCastException e) { log.error("Method cannot be used with field.", e) return null @@ -1479,10 +1479,10 @@ class ActionDelegate { * Finds Petri nets by their MongoDB IDs. * * @param mongoIds list of Petri net identifiers - * @return matching Petri nets, or an empty list when the input is {@code null} + * @return matching Petri nets, or an empty list when the input is {@code null} or {@code empty} */ List<PetriNet> findPetriNets(List<String> mongoIds) { - if(mongoIds == null){ + if(mongoIds == null || mongoIds.empty){ log.warn("Null value detected, returning empty list.") return [] } @@ -1493,10 +1493,10 @@ class ActionDelegate { * Finds Petri nets by their {@link ObjectId} values. * * @param objectIds list of Petri net object identifiers - * @return matching Petri nets, or an empty list when the input is {@code null} + * @return matching Petri nets, or an empty list when the input is {@code null} or {@code empty} */ List<PetriNet> findPetriNetsByObjectIds(List<ObjectId> objectIds) { - if(objectIds == null){ + if(objectIds == null || objectIds.empty){ log.warn("Null value detected, returning empty list.") return [] } diff --git a/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy index 1f7240f5a53..dc10532dbd3 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy @@ -34,7 +34,7 @@ import javax.mail.internet.MimeMessage import static java.util.Base64.* import static org.junit.jupiter.api.Assertions.assertThrows -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest @ActiveProfiles(["test"]) @@ -60,6 +60,9 @@ class ActionDelegateTest { private IPetriNetService petriNetService private static final ACTION_API_NET_IDENTIFIER = "action_api_improvements" + private static final ERROR_MESSAGE_TEMPLATE = "field [|fieldId|] in [|testedMethod|] method returned null" + private static final FIELD_ID_TEMPLATE = "|fieldId|" + private static final TESTED_METHOD_TEMPLATE = "|testedMethod|" @BeforeEach void before() { @@ -349,7 +352,7 @@ class ActionDelegateTest { List<String> transitionIds = ["t1", "t2"] IUser user = userService.getLoggedOrSystem() -// testing "byTransition" variants should be enough, as they call methods, that tak List<Task> instead of List<String> +// testing "byTransition" variants should be enough, as they call methods, that take List<Task> instead of List<String> List<Task> tasks = actionDelegate.assignTasksByTransitions(transitionIds, testCase) assert tasks != null assert tasks.size() == transitionIds.size() @@ -383,73 +386,83 @@ class ActionDelegateTest { }) String expectedMessage = "Could not find Case with id [${deletedCaseId}]" - assertTrue(expectedMessage == e.getMessage()) + assertEquals(expectedMessage, e.getMessage()) } private void assertTaskSearchResults(String fieldId, Case testCase, int sizeToCheck, boolean singleValueField) { actionDelegate.initFieldsMap([(fieldId): fieldId]) + String errorMessageWithFieldId = ERROR_MESSAGE_TEMPLATE.replace(FIELD_ID_TEMPLATE, fieldId) Field field = actionDelegate.map.get(fieldId) String firstTaskId = ([field.value].flatten() as List<String>)[0] Task task = actionDelegate.findTask(field) - assert fieldId && task != null - assert fieldId && task.stringId == firstTaskId + String errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findTask(Field)") + assert task != null : errorMessage + assert task.stringId == firstTaskId : errorMessage task = null List<Task> tasks = actionDelegate.findTasks(field) - assert fieldId && tasks != null - assert fieldId && !tasks.empty - assert fieldId && tasks.size() == sizeToCheck + errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findTasks(Field)") + assert tasks != null : errorMessage + assert !tasks.empty : errorMessage + assert tasks.size() == sizeToCheck : errorMessage if (singleValueField) { - assert fieldId && tasks[0].stringId == firstTaskId + assert tasks[0].stringId == firstTaskId : errorMessage } tasks = null DataField dataField = testCase.getDataField(fieldId) task = actionDelegate.findTask(dataField) - assert fieldId && task != null - assert fieldId && task.stringId == firstTaskId + errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findTask(DataField)") + assert task != null : errorMessage + assert task.stringId == firstTaskId : errorMessage tasks = actionDelegate.findTasks(dataField) - assert fieldId && tasks != null - assert fieldId && !tasks.empty - assert fieldId && tasks.size() == sizeToCheck + errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findTasks(DataField)") + assert tasks != null : errorMessage + assert !tasks.empty : errorMessage + assert tasks.size() == sizeToCheck : errorMessage if (singleValueField) { - assert fieldId && tasks[0].stringId == firstTaskId + assert tasks[0].stringId == firstTaskId : errorMessage } } private void assertCaseSearchResults(String fieldId, Case testCase, int sizeToCheck, boolean singleValueField) { actionDelegate.initFieldsMap([(fieldId): fieldId]) + String errorMessageWithFieldId = ERROR_MESSAGE_TEMPLATE.replace(FIELD_ID_TEMPLATE, fieldId) Field field = actionDelegate.map.get(fieldId) String firstCaseId = ([field.value].flatten() as List<String>)[0] Case searchedCase = actionDelegate.findCase(field) - assert fieldId && searchedCase != null - assert fieldId && searchedCase.stringId == firstCaseId + String errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findCase(Field)") + assert searchedCase != null : errorMessage + assert searchedCase.stringId == firstCaseId : errorMessage searchedCase = null List<Case> searchedCases = actionDelegate.findCases(field) - assert fieldId && searchedCases != null - assert fieldId && !searchedCases.empty - assert fieldId && searchedCases.size() == sizeToCheck + errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findCases(Field)") + assert searchedCases != null : errorMessage + assert !searchedCases.empty : errorMessage + assert searchedCases.size() == sizeToCheck : errorMessage if (singleValueField) { - assert fieldId && searchedCases[0].stringId == firstCaseId + assert searchedCases[0].stringId == firstCaseId : errorMessage } searchedCases = null DataField dataField = testCase.getDataField(fieldId) searchedCase = actionDelegate.findCase(dataField) - assert fieldId && searchedCase != null - assert fieldId && searchedCase.stringId == firstCaseId + errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findCase(DataField)") + assert searchedCase != null: errorMessage + assert searchedCase.stringId == firstCaseId: errorMessage searchedCases = actionDelegate.findCases(dataField) - assert fieldId && searchedCases != null - assert fieldId && !searchedCases.empty - assert fieldId && searchedCases.size() == sizeToCheck + errorMessage = errorMessageWithFieldId.replace(TESTED_METHOD_TEMPLATE, "findCases(DataField)") + assert searchedCases != null: errorMessage + assert !searchedCases.empty: errorMessage + assert searchedCases.size() == sizeToCheck: errorMessage if (singleValueField) { - assert fieldId && searchedCases[0].stringId == firstCaseId + assert searchedCases[0].stringId == firstCaseId: errorMessage } } diff --git a/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml b/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml index 6c201f9ea07..4cd61d922b0 100644 --- a/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml +++ b/src/test/resources/petriNets/NAE-2390_action_api_improvements.xml @@ -2,7 +2,7 @@ <id>action_api_improvements</id> <version>1.0.0</version> <initials>NEW</initials> - <title>New Model + Action API Improvements Test device_hub true true From cc4fa4774b15af44301afca24897e0704f0f10ab Mon Sep 17 00:00:00 2001 From: palajsamuel Date: Thu, 23 Apr 2026 14:47:43 +0200 Subject: [PATCH 059/174] [NAE-2390] Action API Improvements - return value fixed --- .../petrinet/domain/dataset/logic/action/ActionDelegate.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 6d401e02f80..3295f7a5f22 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -949,7 +949,7 @@ class ActionDelegate { return this.findCases([caseRef.value].flatten() as List) } catch (ClassCastException e) { log.error("Method cannot be used with field with id [${caseRef.importId}].", e) - return null + return [] } } From 363ae24085f467ecfc0df4394f1e170ae813ec5a Mon Sep 17 00:00:00 2001 From: palajsamuel Date: Thu, 23 Apr 2026 14:55:52 +0200 Subject: [PATCH 060/174] [NAE-2390] Action API Improvements - javadoc fixes --- .../domain/dataset/logic/action/ActionDelegate.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 3295f7a5f22..39446967837 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -925,7 +925,7 @@ class ActionDelegate { * current action context see other overloads of this action. * *

If the field value is {@code null}, this method returns an empty list.

- *

If the value cannot be converted to case IDs, this method returns {@code null}.

+ *

If the value cannot be converted to case IDs, this method returns an empty list.

* * @param caseRef field whose value contains case IDs, may be of types * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#CASE_REF}, @@ -960,7 +960,7 @@ class ActionDelegate { * action context see other overloads of this action. * *

If the field value is {@code null}, this method returns an empty list.

- *

If the value cannot be converted to case IDs, this method returns {@code null}.

+ *

If the value cannot be converted to case IDs, this method returns an empty list.

* * @param caseRef field whose value contains case IDs, may be of types * {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#CASE_REF}, @@ -1346,7 +1346,7 @@ class ActionDelegate { * Finds tasks by their MongoDB IDs. * * @param mongoIds task identifiers - * @return list of matching tasks, or an empty list when the input is {@code null} org {@code empty} + * @return list of matching tasks, or an empty list when the input is {@code null} or {@code empty} * @see ActionDelegate#findTasks(Field) * @see ActionDelegate#findTasks(DataField) */ From fe4cda6fafad4bb5cb26dc61a03f46b8dcf061fd Mon Sep 17 00:00:00 2001 From: palajsamuel Date: Thu, 30 Apr 2026 12:25:57 +0200 Subject: [PATCH 061/174] [NAE-2060] Merge 6.2.10, 6.4.2 into 6.5.0 - after merge fixes --- .../workflow/domain/menu/MenuItemBody.java | 268 -- .../domain/menu/MenuItemConstants.java | 66 - .../menu/tabbed_case_view_configuration.xml | 19 + .../engine-processes/preference_item.xml | 2686 ----------------- .../export/service/XlsExportServiceTest.java | 4 +- 5 files changed, 21 insertions(+), 3022 deletions(-) delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java delete mode 100644 src/main/resources/petriNets/engine-processes/preference_item.xml diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java deleted file mode 100644 index 5f59e15ccd7..00000000000 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemBody.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.netgrif.application.engine.workflow.domain.menu; - -import com.netgrif.application.engine.petrinet.domain.I18nString; -import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; -import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate; -import com.netgrif.application.engine.workflow.domain.Case; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.annotation.Nullable; -import java.text.Normalizer; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * Class, that holds configurable attributes of menu item. In case of attribute addition, please update also - * {@link MenuItemBody#toDataSet(String, String, boolean)} method. - */ -@Data -@NoArgsConstructor -public class MenuItemBody { - - // generic attributes - private I18nString menuName; - private I18nString tabName; - private String menuIcon = "filter_none"; - private String tabIcon; - private String uri; - private String identifier; - private Case filter; - private Map allowedRoles; - private Map bannedRoles; - private boolean useTabIcon = true; - private boolean useCustomView = false; - private String customViewSelector; - - // case view attributes - private String caseViewSearchType = "fulltext_advanced"; - private String createCaseButtonTitle; - private String createCaseButtonIcon = "add"; - private boolean caseRequireTitleInCreation = true; - private boolean showCreateCaseButton = true; - private String bannedNetsInCreation; - private boolean caseShowMoreMenu = false; - private boolean caseAllowHeaderTableMode = true; - private List caseHeadersMode = new ArrayList<>(List.of("sort", "edit", "search")); - private String caseHeadersDefaultMode = "sort"; - private List caseDefaultHeaders; - private boolean caseIsHeaderModeChangeable = true; - private boolean caseUseDefaultHeaders = true; - private boolean caseAllowExport = false; - - // task view attributes - private Case additionalFilter; - private boolean mergeFilters = true; - private String taskViewSearchType = "fulltext_advanced"; - private List taskHeadersMode = new ArrayList<>(List.of("sort", "edit")); - private String taskHeadersDefaultMode = "sort"; - private boolean taskIsHeaderModeChangeable = true; - private boolean taskAllowHeaderTableMode = true; - private boolean taskUseDefaultHeaders = true; - private List taskDefaultHeaders; - private boolean taskShowMoreMenu = true; - - public MenuItemBody(I18nString name, String icon) { - this.menuName = name; - this.tabName = name; - this.menuIcon = icon; - this.tabIcon = icon; - } - - public MenuItemBody(I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { - this.menuName = menuName; - this.tabName = tabName; - this.menuIcon = menuIcon; - this.tabIcon = tabIcon; - } - - public MenuItemBody(String uri, String identifier, I18nString name, String icon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = name; - this.tabName = name; - this.menuIcon = icon; - this.tabIcon = icon; - } - - public MenuItemBody(String uri, String identifier, I18nString menuName, I18nString tabName, String menuIcon, String tabIcon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = menuName; - this.tabName = tabName; - this.menuIcon = menuIcon; - this.tabIcon = tabIcon; - } - - public MenuItemBody(String uri, String identifier, String name, String icon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = new I18nString(name); - this.tabName = new I18nString(name); - this.menuIcon = icon; - this.tabIcon = icon; - } - - public MenuItemBody(String uri, String identifier, String menuName, String tabName, String menuIcon, String tabIcon) { - this.uri = uri; - this.identifier = identifier; - this.menuName = new I18nString(menuName); - this.tabName = new I18nString(tabName); - this.menuIcon = menuIcon; - this.tabIcon = tabIcon; - } - - private static void putDataSetEntry(Map> dataSet, MenuItemConstants fieldId, FieldType fieldType, - @Nullable Object fieldValue) { - Map fieldMap = new LinkedHashMap<>(); - fieldMap.put("type", fieldType.getName()); - fieldMap.put("value", fieldValue); - dataSet.put(fieldId.getAttributeId(), fieldMap); - } - - private static String sanitize(String input) { - if (input == null) { - return null; - } - return Normalizer.normalize(input.trim(), Normalizer.Form.NFD) - .replaceAll("[^\\p{ASCII}]", "") - .replaceAll("\\p{InCombiningDiacriticalMarks}+", "") - .replaceAll("[\\W-]+", "-") - .toLowerCase(); - } - - public String getIdentifier() { - return sanitize(this.identifier); - } - - public void setMenuName(I18nString name) { - this.menuName = name; - } - - public void setMenuName(String name) { - this.menuName = new I18nString(name); - } - - public void setTabName(I18nString name) { - this.tabName = name; - } - - public void setTabName(String name) { - this.tabName = new I18nString(name); - } - - /** - * Transforms attributes into dataSet for {@link ActionDelegate#setData} - * - * @return created dataSet from attributes - */ - public Map> toDataSet() { - return toDataSet(null, null, true); - } - - /** - * Transforms attributes into dataSet for {@link ActionDelegate#setData} - * - * @param parentId id of parent menu item instance - * @param nodePath uri, that represents the menu item (f.e.: "/myItem1/myItem2") - * @return created dataSet from attributes - */ - public Map> toDataSet(String parentId, String nodePath) { - return toDataSet(parentId, nodePath, false); - } - - private Map> toDataSet(String parentId, String nodePath, boolean ignoreParentId) { - Map> dataSet = new LinkedHashMap<>(); - - // GENERIC - ArrayList filterIdCaseRefValue = new ArrayList<>(); - if (this.filter != null) { - filterIdCaseRefValue.add(this.filter.getStringId()); - } - ArrayList parentIdCaseRef = new ArrayList<>(); - if (parentId != null) { - parentIdCaseRef.add(parentId); - } - - if (nodePath != null) { - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_NODE_PATH, FieldType.TEXT, nodePath); - } - if (!ignoreParentId) { - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID, FieldType.CASE_REF, parentIdCaseRef); - } - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_NAME, FieldType.I18N, this.menuName); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_MENU_ICON, FieldType.TEXT, this.menuIcon); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_NAME, FieldType.I18N, this.tabName); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TAB_ICON, FieldType.TEXT, this.tabIcon); - if (this.identifier != null) { - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_IDENTIFIER, FieldType.TEXT, this.getIdentifier()); - } - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_FILTER_CASE, FieldType.CASE_REF, filterIdCaseRefValue); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_TAB_ICON, FieldType.BOOLEAN, this.useTabIcon); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW, FieldType.BOOLEAN, - this.useCustomView); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR, FieldType.TEXT, - this.customViewSelector); - - // CASE - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, - this.caseViewSearchType); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.TEXT, - this.createCaseButtonTitle); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, - this.createCaseButtonIcon); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, - this.caseRequireTitleInCreation); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, - this.showCreateCaseButton); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, - this.bannedNetsInCreation); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_SHOW_MORE_MENU, FieldType.BOOLEAN, - this.caseShowMoreMenu); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, - this.caseAllowHeaderTableMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_HEADERS_MODE, FieldType.MULTICHOICE_MAP, - this.caseHeadersMode == null ? new ArrayList<>() : this.caseHeadersMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, - this.caseHeadersDefaultMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS, FieldType.TEXT, - this.caseDefaultHeaders != null ? String.join(",", this.caseDefaultHeaders) : null); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, - this.caseIsHeaderModeChangeable); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, - this.caseUseDefaultHeaders); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CASE_ALLOW_EXPORT, FieldType.BOOLEAN, - this.caseAllowExport); - - // TASK - ArrayList additionalFilterIdCaseRefValue = new ArrayList<>(); - if (this.additionalFilter != null) { - additionalFilterIdCaseRefValue.add(this.additionalFilter.getStringId()); - } - - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_ADDITIONAL_FILTER_CASE, FieldType.CASE_REF, - additionalFilterIdCaseRefValue); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_MERGE_FILTERS, FieldType.BOOLEAN, - this.mergeFilters); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, - this.taskViewSearchType); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_HEADERS_MODE, FieldType.MULTICHOICE_MAP, - this.taskHeadersMode == null ? new ArrayList<>() : this.taskHeadersMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, - this.taskHeadersDefaultMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, - this.taskIsHeaderModeChangeable); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, - this.taskAllowHeaderTableMode); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_USE_TASK_DEFAULT_HEADERS, FieldType.BOOLEAN, - this.taskUseDefaultHeaders); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS, FieldType.TEXT, - this.taskDefaultHeaders != null ? String.join(",", this.taskDefaultHeaders) : null); - putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_TASK_SHOW_MORE_MENU, FieldType.BOOLEAN, - this.taskShowMoreMenu); - - return dataSet; - } -} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java deleted file mode 100644 index b449bcc809a..00000000000 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/menu/MenuItemConstants.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.netgrif.application.engine.workflow.domain.menu; - -import lombok.Getter; - -/** - * Enumeration for menu items. It contains any constants needed in application. - */ -public enum MenuItemConstants { - - // FIELDS - PREFERENCE_ITEM_FIELD_NEW_FILTER_ID("new_filter_id"), - PREFERENCE_ITEM_FIELD_FILTER_CASE("filter_case"), - PREFERENCE_ITEM_FIELD_PARENT_ID("parentId"), - PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS("childItemIds"), - PREFERENCE_ITEM_FIELD_HAS_CHILDREN("hasChildren"), - PREFERENCE_ITEM_FIELD_CASE_DEFAULT_HEADERS("case_default_headers"), - PREFERENCE_ITEM_FIELD_TASK_DEFAULT_HEADERS("task_default_headers"), - PREFERENCE_ITEM_FIELD_IDENTIFIER("menu_item_identifier"), - PREFERENCE_ITEM_FIELD_APPEND_MENU_ITEM("append_menu_item_stringId"), - PREFERENCE_ITEM_FIELD_ALLOWED_ROLES("allowed_roles"), - PREFERENCE_ITEM_FIELD_BANNED_ROLES("banned_roles"), - PREFERENCE_ITEM_FIELD_MENU_NAME("menu_name"), - PREFERENCE_ITEM_FIELD_MENU_ICON("menu_icon"), - PREFERENCE_ITEM_FIELD_TAB_NAME("tab_name"), - PREFERENCE_ITEM_FIELD_USE_TAB_ICON("use_tab_icon"), - PREFERENCE_ITEM_FIELD_TAB_ICON("tab_icon"), - PREFERENCE_ITEM_FIELD_NODE_PATH("nodePath"), - PREFERENCE_ITEM_FIELD_NODE_NAME("nodeName"), - PREFERENCE_ITEM_FIELD_DUPLICATE_TITLE("duplicate_new_title"), - PREFERENCE_ITEM_FIELD_DUPLICATE_IDENTIFIER("duplicate_view_identifier"), - PREFERENCE_ITEM_FIELD_DUPLICATE_RESET_CHILD_ITEM_IDS("duplicate_reset_childItemIds"), - PREFERENCE_ITEM_FIELD_REQUIRE_TITLE_IN_CREATION("case_require_title_in_creation"), - PREFERENCE_ITEM_FIELD_USE_CUSTOM_VIEW("use_custom_view"), - PREFERENCE_ITEM_FIELD_CUSTOM_VIEW_SELECTOR("custom_view_selector"), - PREFERENCE_ITEM_FIELD_CASE_VIEW_SEARCH_TYPE("case_view_search_type"), - PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_TITLE("create_case_button_title"), - PREFERENCE_ITEM_FIELD_CREATE_CASE_BUTTON_ICON("create_case_button_icon"), - PREFERENCE_ITEM_FIELD_BANNED_NETS_IN_CREATION("case_banned_nets_in_creation"), - PREFERENCE_ITEM_FIELD_SHOW_CREATE_CASE_BUTTON("show_create_case_button"), - PREFERENCE_ITEM_FIELD_CASE_SHOW_MORE_MENU("case_show_more_menu"), - PREFERENCE_ITEM_FIELD_CASE_ALLOW_HEADER_TABLE_MODE("case_allow_header_table_mode"), - PREFERENCE_ITEM_FIELD_CASE_HEADERS_MODE("case_headers_mode"), - PREFERENCE_ITEM_FIELD_CASE_HEADERS_DEFAULT_MODE("case_headers_default_mode"), - PREFERENCE_ITEM_FIELD_CASE_IS_HEADER_MODE_CHANGEABLE("case_is_header_mode_changeable"), - PREFERENCE_ITEM_FIELD_USE_CASE_DEFAULT_HEADERS("case_is_header_mode_changeable"), - PREFERENCE_ITEM_FIELD_CASE_ALLOW_EXPORT("case_allow_export"), - PREFERENCE_ITEM_FIELD_ADDITIONAL_FILTER_CASE("additional_filter_case"), - PREFERENCE_ITEM_FIELD_MERGE_FILTERS("merge_filters"), - PREFERENCE_ITEM_FIELD_TASK_VIEW_SEARCH_TYPE("task_view_search_type"), - PREFERENCE_ITEM_FIELD_TASK_HEADERS_MODE("task_headers_mode"), - PREFERENCE_ITEM_FIELD_TASK_HEADERS_DEFAULT_MODE("task_headers_default_mode"), - PREFERENCE_ITEM_FIELD_TASK_IS_HEADER_MODE_CHANGEABLE("task_is_header_mode_changeable"), - PREFERENCE_ITEM_FIELD_TASK_ALLOW_HEADER_TABLE_MODE("task_allow_header_table_mode"), - PREFERENCE_ITEM_FIELD_USE_TASK_DEFAULT_HEADERS("use_task_default_headers"), - PREFERENCE_ITEM_FIELD_TASK_SHOW_MORE_MENU("task_show_more_menu"), - - // TRANSITIONS - PREFERENCE_ITEM_SETTINGS_TRANS_ID("item_settings"), - PREFERENCE_ITEM_FIELD_INIT_TRANS_ID("initialize"); - @Getter - private final String attributeId; - - MenuItemConstants(String attributeId) { - this.attributeId = attributeId; - } -} diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 3e1adad7aa7..d1c28db279c 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -393,6 +393,11 @@ </data> + <data type="boolean" immediate="true"> + <id>case_allow_export</id> + <title name="case_allow_export">Allow export? +
+ Názov tlačidla "Nová inštancia" @@ -428,6 +433,7 @@ Vybrať zobrazenie Nastavenie Asociované zobrazenie + Povoliť export? Schaltflächentitel "Neuer Fall" @@ -463,6 +469,7 @@ Wählen Sie einen Ansichtstyp Einstellungen zugehörige Ansicht + Erlaube Export? @@ -894,6 +901,18 @@ outline + + case_allow_export + + hidden + + + 0 + 999 + 1 + 1 + +
diff --git a/src/main/resources/petriNets/engine-processes/preference_item.xml b/src/main/resources/petriNets/engine-processes/preference_item.xml deleted file mode 100644 index b3a4b556764..00000000000 --- a/src/main/resources/petriNets/engine-processes/preference_item.xml +++ /dev/null @@ -1,2686 +0,0 @@ - - preference_item - PRI - Preference Item - check_box_outline_blank - true - false - false - - system - - true - true - true - - - - admin - - true - true - true - - - - default - - false - false - true - - - - - preference_item_delete - - - removeItemChildCases(useCase) - - - - - - system - System - - - admin - Admin - - - { com.netgrif.application.engine.workflow.domain.Case useCase -> - - def childCaseIds = useCase.dataSet['childItemIds'].value - if (childCaseIds == null || childCaseIds.isEmpty()) { - return - } - - removeChildItemFromParent(useCase.dataSet['parentId'].value[0], useCase) - - def childCases = workflowService.findAllById(childCaseIds) - async.run { - childCases.each { - workflowService.deleteCase(it) - } - } - } - - - { - com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete, - com.netgrif.application.engine.petrinet.domain.dataset.TaskField previewTaskRef, - com.netgrif.application.engine.petrinet.domain.dataset.CaseField selectedFilterRef, - com.netgrif.application.engine.petrinet.domain.dataset.ButtonField updateBtn, - com.netgrif.application.engine.petrinet.domain.Transition trans, - boolean taskTypeOnly - -> - if (filterAutocomplete.getOptions().containsKey(filterAutocomplete.value)) { - change previewTaskRef value { - return [findTask({it.caseId.eq(filterAutocomplete.value).and(it.transitionId.eq("view_filter"))}).stringId] - } - make updateBtn,editable on trans when { true } - } else { - change filterAutocomplete options { - def findAllPredicate - if (taskTypeOnly) { - findAllPredicate = { filterCase -> - !selectedFilterRef.value.contains(filterCase.stringId) && - filterCase.dataSet["filter_type"].value == "Task" - } - } else { - findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) } - } - return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") - .findAll(findAllPredicate) - .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) - } - change previewTaskRef value { [] } - make updateBtn,visible on trans when { true } - } - } - - - { - com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField toBeUpdated, - com.netgrif.application.engine.petrinet.domain.dataset.MultichoiceMapField valueSelector, - com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField optionsHolder - -> - def existingOptions = optionsHolder.options - def selectedValues = valueSelector.value - def newOptions = [:] - - if (selectedValues != null) { - existingOptions.each { key, value -> - if (selectedValues.contains(key)) { - newOptions.put(key, value) - } - } - } - - if (!newOptions.containsKey(toBeUpdated.value)) { - change toBeUpdated value { null } - } - - change toBeUpdated options { newOptions } - } - - - - parentId - - <allowedNets> - <allowedNet>preference_item</allowedNet> - </allowedNets> - </data> - <data type="text"> - <id>move_previous_dest_uri</id> - <title/> - </data> - <data type="multichoice_map"> - <id>move_dest_uri</id> - <title name="move_dest_uri">Destination URI - List of nodes representing destination URI - - autocomplete - - - moveDestUri: f.move_dest_uri; - - String uriNodeId = elasticCaseService.findUriNodeId(useCase) - def node = uriService.findById(uriNodeId) - updateMultichoiceWithCurrentNode(moveDestUri, node) - - - prevDestUri: f.move_previous_dest_uri, - moveDestUri: f.move_dest_uri; - - String newUri = moveDestUri.value.join("/") - newUri = newUri.replace("//","/") - - String corrected = getCorrectedUri(newUri) - - if (corrected == newUri) { - def node = uriService.findByUri(newUri) - change moveDestUri options { findOptionsBasedOnSelectedNode(node) } - } else { - change moveDestUri value { splitUriPath(corrected) } - } - - - - move_dest_uri_new_node - New node to be added - Enter new node name - - - move_add_node - - <placeholder name="move_add_node">Add</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - newNodeName: f.move_dest_uri_new_node, - selectedUri: f.move_dest_uri; - - if (newNodeName.value == null || newNodeName.value == "") { - return - } - - String prefixUri = selectedUri.value.join("/") - prefixUri = prefixUri.replace("//","/") - - String newUri = prefixUri + uriService.getUriSeparator() + newNodeName.value - def newNode = uriService.getOrCreate(newUri, com.netgrif.application.engine.petrinet.domain.UriContentType.CASE) - - change selectedUri value { splitUriPath(newNode.uriPath) } - - change newNodeName value { null } - </action> - </data> - <data type="i18n"> - <id>duplicate_new_title</id> - <title name="duplicate_new_title">Title of duplicated view - - - duplicate_view_identifier - View identifier - Must be unique - - - childItemIds - - <allowedNets> - <allowedNet>preference_item</allowedNet> - </allowedNets> - </data> - <data type="taskRef"> - <id>childItemForms</id> - <title/> - </data> - <data type="boolean" immediate="true"> - <id>hasChildren</id> - <title/> - </data> - <data type="button"> - <id>duplicate_reset_childItemIds</id> - <title/> - <action trigger="set"> - hasChildren: f.hasChildren, - childItemIds: f.childItemIds; - - change childItemIds value { [] } - change hasChildren value { false } - </action> - </data> - <data type="text" immediate="true"> - <id>menu_item_identifier</id> - <title name="menu_item_identifier">Menu item identifier - - - nodePath - Item URI - - 0 - - - nodePath: f.nodePath, - menu_item_identifier: f.menu_item_identifier; - - change menu_item_identifier value { - def idx = nodePath.value.lastIndexOf(uriService.getUriSeparator()) - return nodePath.value.substring(idx + 1) - } - - - - - - - - menu_icon - Menu icon identifier - Material icon identifier. List of icons with identifiers is available online. - - icon: f.this, - iconPreview: f.menu_icon_preview; - - changeCaseProperty "icon" about { icon.value; } - - if (icon.value == "") { - change iconPreview value {"""]]>} - return; - } - - change iconPreview value { - """]]> + icon.value + """]]> - } - - - - menu_icon_preview - Menu icon preview - - htmltextarea - - - - menu_name_as_visible - Name of the item - Is shown in the menu - - autocomplete - - - - menu_name - Name of the item - Will be shown in the menu - - menu_name_as_visible: f.menu_name_as_visible, - name: f.menu_name; - - changeCaseProperty "title" about { name.value } - change menu_name_as_visible choices { [name.value] } - change menu_name_as_visible value { name.value } - - - - tab_icon - Tab icon identifier - Material icon identifier. List of icons with identifiers is available online. - - icon: f.this, - iconPreview: f.tab_icon_preview; - - if (icon.value == "") { - change iconPreview value {"""]]>} - return; - } - - change iconPreview value { - """]]> + icon.value + """]]> - } - - - - tab_icon_preview - Tab icon preview - - htmltextarea - - - - use_tab_icon - Display tab icon? - true - - - tab_name - Name of the item - Will be shown in tab - - - add_allowed_roles - - <placeholder name="allow_roles">Allow view for roles</placeholder> - <action trigger="set"> - allowedRoles: f.allowed_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change allowedRoles options {return configurableMenuService.addSelectedRoles(allowedRoles, processesAvailable, rolesAvailable)} - - change rolesAvailable value {[]} - change rolesAvailable options {[:]} - change processesAvailable value {null} - </action> - </data> - <data type="button"> - <id>remove_allowed_roles</id> - <title/> - <placeholder name="remove_from_allowed_roles">Remove from allowed roles</placeholder> - <action trigger="set"> - allowedRoles: f.allowed_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change allowedRoles options {return configurableMenuService.removeSelectedRoles(allowedRoles)} - - change allowedRoles value {[]} - change rolesAvailable value {[]} - change rolesAvailable options {[:]} - change processesAvailable value {null} - </action> - </data> - <data type="button"> - <id>add_banned_roles</id> - <title/> - <placeholder name="ban_roles">Ban view for roles</placeholder> - <action trigger="set"> - bannedRoles: f.banned_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change bannedRoles options {return configurableMenuService.addSelectedRoles(bannedRoles, processesAvailable, rolesAvailable)} - - change rolesAvailable value {[]} - change rolesAvailable options {[:]} - change processesAvailable value {null} - </action> - </data> - <data type="button"> - <id>remove_banned_roles</id> - <title/> - <placeholder name="remove_from_banned_roles">Remove from banned roles</placeholder> - <action trigger="set"> - bannedRoles: f.banned_roles, - processesAvailable: f.processes_available, - rolesAvailable: f.roles_available; - - change bannedRoles options { return configurableMenuService.removeSelectedRoles(bannedRoles) } - - change bannedRoles value { [] } - change rolesAvailable value { [] } - change rolesAvailable options { [:] } - change processesAvailable value { null } - </action> - </data> - <data type="enumeration_map" immediate="true"> - <id>processes_available</id> - <title name="available_processes">Your processes - Select a process containing roles you wish to add to allowed or banned roles lists. - - processes: f.this; - - change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } - - - processes: f.this, - allowedRoles: f.allowed_roles, - bannedRoles: f.banned_roles, - rolesAvailable: f.roles_available; - - if (processes.value != null) { - change rolesAvailable options { return configurableMenuService.getAvailableRolesFromNet(processes, allowedRoles, bannedRoles) } - } else { - change rolesAvailable options { [:] } - } - change rolesAvailable value { [] } - - - - roles_available - Available roles from selected process - - - allowed_roles - Allowed roles - List of roles allowed to view this menu entry. - - [:] - - - - banned_roles - Banned roles - List of roles not allowed to view this menu entry. - - [:] - - - - selected_filter_preview - - </data> - <data type="taskRef"> - <id>current_filter_preview</id> - <title/> - </data> - <data type="i18n"> - <id>filter_header</id> - <title/> - <init name="filter_header">Current filter</init> - <component> - <name>divider</name> - </component> - </data> - <data type="text"> - <id>new_filter_id</id> - <title/> - </data> - <data type="enumeration_map"> - <id>filter_autocomplete_selection</id> - <title name="filter_autocomplete_selection">Select new filter - - autocomplete_dynamic - - - trans: t.item_settings, - useCustomView: f.use_custom_view, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans, false) - - make update_filter,hidden on trans when { useCustomView.value } - - - trans: t.item_settings, - useCustomView: f.use_custom_view, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans, false) - - make update_filter,hidden on trans when { useCustomView.value } - - - - update_filter - - <placeholder name="update_filter">Update view with selected filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - trans: t.item_settings, - update_filter: f.update_filter, - filter_case: f.filter_case, - filterAutocomplete: f.filter_autocomplete_selection; - - change filter_case value { [filterAutocomplete.value] } - change filterAutocomplete value { "" } - make update_filter,visible on trans when { true } - </action> - </data> - <data type="caseRef"> - <id>filter_case</id> - <title/> - <action trigger="set"> - additionalFilterCase: f.additional_filter_case, - additionalAutocomplete: f.additional_filter_autocomplete_selection, - additionalUpdate: f.update_additional_filter, - additionalFilterPreview: f.selected_additional_filter_preview, - mergeFilters: f.merge_filters, - filterHeader: f.filter_header, - currentAdditionalFilterPreview: f.current_additional_filter_preview, - taskSettingsTrans: t.task_view_settings, - settingsTrans: t.item_settings, - caseViewHeader: f.case_view_header, - caseViewSettingsTaskRef: f.case_view_settings_taskRef, - taskViewHeader: f.task_view_header, - taskViewSettingsTaskRef: f.task_view_settings_taskRef, - filterTaskRef: f.current_filter_preview, - filterCaseRef: f.filter_case; - - if (filterCaseRef.value == null || filterCaseRef.value == []) { - return - } - - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - change filterTaskRef value {return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId]} - - if (filterCase.dataSet["filter_type"].value == "Case") { - make caseViewHeader,editable on settingsTrans when { true } - make caseViewSettingsTaskRef,editable on settingsTrans when { true } - - make additionalAutocomplete,editable on taskSettingsTrans when { true } - make additionalUpdate,visible on taskSettingsTrans when { true } - make additionalFilterPreview,visible on taskSettingsTrans when { true } - make mergeFilters,visible on taskSettingsTrans when { true } - make filterHeader,visible on taskSettingsTrans when { true } - make currentAdditionalFilterPreview,visible on taskSettingsTrans when { true } - } else { - make caseViewHeader,hidden on settingsTrans when { true } - make caseViewSettingsTaskRef,hidden on settingsTrans when { true } - - make additionalAutocomplete,hidden on taskSettingsTrans when { true } - make additionalUpdate,hidden on taskSettingsTrans when { true } - make additionalFilterPreview,hidden on taskSettingsTrans when { true } - make mergeFilters,hidden on taskSettingsTrans when { true } - make filterHeader,hidden on taskSettingsTrans when { true } - make currentAdditionalFilterPreview,hidden on taskSettingsTrans when { true } - } - make taskViewHeader,editable on settingsTrans when { true } - make taskViewSettingsTaskRef,editable on settingsTrans when { true } - - change additionalFilterCase value { [] } - </action> - <allowedNets> - <allowedNet>filter</allowedNet> - </allowedNets> - </data> - <data type="boolean" immediate="true"> - <id>use_custom_view</id> - <title name="use_custom_view">Use custom view? - false - - - custom_view_selector - Custom view configuration selector - Example: "demo-tabbed-views" - - - - - case_view_search_type - Search type for case view - - - - - - fulltext_advanced - - - create_case_button_title - "New case" button title - - - create_case_button_icon_preview - Icon preview - add]]> - - htmltextarea - - - - create_case_button_icon - "New case" button icon identifier - add - - create_case_button_icon_preview: f.create_case_button_icon_preview, - create_case_button_icon: f.create_case_button_icon; - - - if (create_case_button_icon.value == "") { - change create_case_button_icon_preview value {"""]]>} - return; - } - - change create_case_button_icon_preview value { - """]]> + create_case_button_icon.value + """]]> - } - - - - show_create_case_button - Show create case button? - true - - - case_require_title_in_creation - Require title input in case creation? - true - - - case_banned_nets_in_creation - Banned processes for creation - Write down process identifiers separated by comma. Example: mynet1,mynet2 - - bannedNets: f.this; - - String trimmed = bannedNets.value?.replaceAll("\\s","") - if (bannedNets.value != trimmed) { - change bannedNets value { trimmed } - } - - - - case_view_header - - <init name="case_view_header">Case view</init> - <component> - <name>divider</name> - </component> - </data> - <data type="taskRef"> - <id>case_view_settings_taskRef</id> - <title/> - <init>case_view_settings</init> - </data> - <data type="boolean" immediate="true"> - <id>case_show_more_menu</id> - <title name="case_show_more_menu">Show more menu for case item? - false - - - case_allow_header_table_mode - Allow table mode for headers? - true - - - case_headers_mode - Header mode - - - - - - sort,edit,search - - headersMode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode, - holder: f.case_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - headersMode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode, - holder: f.case_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - - case_headers_default_mode - Default header mode - - - - - - sort - - - case_headers_options_holder - - <options> - <option key="sort" name="sort">Sort</option> - <option key="search" name="search">Search</option> - <option key="edit" name="edit">Edit</option> - </options> - </data> - <data type="boolean" immediate="true"> - <id>case_is_header_mode_changeable</id> - <title name="is_header_mode_changeable">Can header mode be changed? - true - - - use_case_default_headers - Use custom default headers? - true - - - case_default_headers - Set default headers - Example: "meta-title,meta-visualId" - - defaultHeaders: f.this; - - String trimmed = defaultHeaders.value?.replaceAll("\\s","") - if (defaultHeaders.value != trimmed) { - change defaultHeaders value { trimmed } - } - - - - - - selected_additional_filter_preview - - </data> - <data type="taskRef"> - <id>current_additional_filter_preview</id> - <title/> - </data> - <data type="enumeration_map"> - <id>additional_filter_autocomplete_selection</id> - <title name="filter_autocomplete_selection">Select new filter - - autocomplete_dynamic - - - trans: t.task_view_settings, - filterAutocomplete: f.this, - filterCase: f.additional_filter_case, - updateFilter: f.update_additional_filter, - previewTaskRef: f.selected_additional_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filterCase, updateFilter, trans, true) - - - trans: t.task_view_settings, - filterAutocomplete: f.this, - filterCase: f.additional_filter_case, - updateFilter: f.update_additional_filter, - previewTaskRef: f.selected_additional_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filterCase, updateFilter, trans, true) - - - - update_additional_filter - - <placeholder name="update_filter">Update view with selected filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - trans: t.task_view_settings, - updateFilter: f.update_additional_filter, - filterCase: f.additional_filter_case, - filterAutocomplete: f.additional_filter_autocomplete_selection; - - change filterCase value { [filterAutocomplete.value] } - change filterAutocomplete value { "" } - make updateFilter,visible on trans when { true } - </action> - </data> - <data type="button"> - <id>remove_additional_filter</id> - <title/> - <placeholder name="remove_additional_filter">Remove additional filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - filterCase: f.additional_filter_case; - - change filterCase value { [] } - </action> - </data> - <data type="caseRef"> - <id>additional_filter_case</id> - <title/> - <action trigger="set"> - taskViewTrans: t.task_view_settings, - mergeFilters: f.merge_filters, - filterHeader: f.filter_header, - filterTaskRef: f.current_additional_filter_preview, - removeButton: f.remove_additional_filter, - filterCaseRef: f.additional_filter_case; - - if (filterCaseRef.value[0] == null) { - make mergeFilters,hidden on taskViewTrans when { true } - make filterHeader,hidden on taskViewTrans when { true } - make removeButton,hidden on taskViewTrans when { true } - change filterTaskRef value { [] } - return - } - - def filterCase = findCase({ it._id.eq(filterCaseRef.value[0]) }) - change filterTaskRef value { return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] } - make mergeFilters,editable on taskViewTrans when { true } - make filterHeader,visible on taskViewTrans when { true } - make removeButton,editable on taskViewTrans when { true } - </action> - <allowedNets> - <allowedNet>filter</allowedNet> - </allowedNets> - </data> - <data type="boolean" immediate="true"> - <id>merge_filters</id> - <title name="merge_filters">Merge with base filter? - true - - - task_view_settings_taskRef - - <init>task_view_settings</init> - </data> - <data type="i18n"> - <id>task_view_header</id> - <title/> - <init name="task_view_header">Task view</init> - <component> - <name>divider</name> - </component> - </data> - <data type="enumeration_map" immediate="true"> - <id>task_view_search_type</id> - <title name="task_view_search_type">Search type for task view - - - - - - fulltext_advanced - - - task_headers_mode - Header mode - - - - - sort,edit - - headersMode: f.task_headers_mode, - defaultMode: f.task_headers_default_mode, - holder: f.task_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - headersMode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode, - holder: f.case_headers_options_holder; - - updateOptionsBasedOnValue(defaultMode, headersMode, holder) - - - - task_headers_default_mode - Default header mode - - - - - sort - - - task_headers_options_holder - - <options> - <option key="sort" name="sort">Sort</option> - <option key="edit" name="edit">Edit</option> - </options> - </data> - <data type="boolean" immediate="true"> - <id>task_is_header_mode_changeable</id> - <title name="is_header_mode_changeable">Can header mode be changed? - true - - - task_allow_header_table_mode - Allow table mode for headers? - true - - - use_task_default_headers - Use custom default headers? - true - - - task_default_headers - Set default headers - Example: "meta-title,meta-user" - - defaultHeaders: f.this; - - String trimmed = defaultHeaders.value?.replaceAll("\\s","") - if (defaultHeaders.value != trimmed) { - change defaultHeaders value { trimmed } - } - - - - task_show_more_menu - Show more menu for task item? - true - - - order_down - - <placeholder>south</placeholder> - <component> - <name>icon</name> - <property key="stretch">true</property> - </component> - <action trigger="set"> - parentId: f.parentId; - - def parentCase = workflowService.findOne(parentId.value[0]) - def taskId = useCase.tasks.find { it.transition == "row_for_ordering" }.task - def taskRefValue = parentCase.dataSet['childItemForms'].value - int taskRefValueSize = taskRefValue.size() - def caseRefValue = parentCase.dataSet['childItemIds'].value - int caseRefValueSize = caseRefValue.size() - - int idxInTaskRef = taskRefValue.indexOf(taskId) - if (idxInTaskRef < taskRefValueSize - 1) { - Collections.swap(taskRefValue, idxInTaskRef, idxInTaskRef + 1) - } - - int idxInCaseRef = caseRefValue.indexOf(useCase.stringId) - if (idxInCaseRef < caseRefValueSize - 1) { - Collections.swap(caseRefValue, idxInCaseRef, idxInCaseRef + 1) - } - - setData("children_order", parentCase, [ - "childItemForms" : [ - "value" : taskRefValue, - "type" : "taskRef" - ], - "childItemIds" : [ - "value" : caseRefValue, - "type" : "caseRef" - ] - ]) - </action> - </data> - <data type="button"> - <id>order_up</id> - <title/> - <placeholder>north</placeholder> - <component> - <name>icon</name> - <property key="stretch">true</property> - </component> - <action trigger="set"> - parentId: f.parentId; - - def parentCase = workflowService.findOne(parentId.value[0]) - def taskId = useCase.tasks.find { it.transition == "row_for_ordering" }.task - def taskRefValue = parentCase.dataSet['childItemForms'].value - def caseRefValue = parentCase.dataSet['childItemIds'].value - - int idxInTaskRef = taskRefValue.indexOf(taskId) - if (idxInTaskRef > 0) { - Collections.swap(taskRefValue, idxInTaskRef - 1, idxInTaskRef) - } else { - return - } - - int idxInCaseRef = caseRefValue.indexOf(useCase.stringId) - if (idxInCaseRef > 0) { - Collections.swap(caseRefValue, idxInCaseRef - 1, idxInCaseRef) - } else { - return - } - - setData("children_order", parentCase, [ - "childItemForms" : [ - "value" : taskRefValue, - "type" : "taskRef" - ], - "childItemIds" : [ - "value" : caseRefValue, - "type" : "caseRef" - ] - ]) - </action> - </data> - <data type="boolean" immediate="true"> - <id>case_allow_export</id> - <title name="case_allow_export">Allow export? - - - - - Náhľad ikony - Identifikátor ikony - Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. - Pridaj k povoleným roliam - Odstráň z povolených rolí - Pridaj k zakázaným roliam - Odstráň zo zakázaných rolí - Vaše procesy - Vyberte proces obsahujúci roly ktoré chcete pridať do zoznamu povolených alebo zakázaných rolí. - Dostupné roly - Názov tlačidla "Nová inštancia" - Identifikátor ikony tlačidla "Nová inštancia" - Náhľad ikony - Predvolené hlavičky - Napríklad: "meta-title,meta-visualId" - Zvoľte nový filter - Cieľové URI - Názov duplikovanej položky - Identifikátor duplikovanej položky - Musí byť jedinečný - Názov položky - Bude zobrazený v menu - Identifikátor ikony v karte - Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. - Zobraziť ikonu v karte? - Názov položky - Bude zobrazený v karte - Aktualizovať zobrazenie s vybraným filtrom - Použiť vlastné zobrazenie? - Konfiguračný identifikátor vlastného zobrazenia - Napríklad: "demo-tabbed-views" - Typ vyhľadávania prípadov - Skryté - Fulltext - Fulltext a rozšírené - Vyžadovať názov inštancii pri vytváraní? - Zakázané siete pri vytváraní - Uveďte identifikátory procesov oddelené čiarkou. Napríklad: mynet1,mynet2 - Zobraziť tlačidlo na vytvorenie prípadu? - Zobrazovať menu pre prípadovú položku? - Zoraďovanie - Vyhľadávanie - Upravovanie - Zjednotiť filter so základným filtrom? - Typ vyhľadávania úloh - Mód hlavičiek - Predvolený mód hlavičiek - Môže byť mód hlavičiek zmenený? - Povoliť tabuľkový mód pre hlavičky? - Použiť vlastné predvolené hlavičky? - Predvolené hlavičky - Napríklad: "meta-title,meta-user" - Zobrazovať menu pre úlohovú položku? - Nastavenie položky - Roly - Filter - Presunúť položku - Presunúť - Duplikovať položku - Duplikovať - Dodatočný filter - Súčasný filter - Zobrazenie prípadov - Zobrazenie úloh - Povolené roly - Zoznam povolených rolí, ktoré môžu zobraziť túto položku - Zakázané roly - Zoznam zakázaných rolí, ktoré nemôžu zobraziť túto položku - Všeobecné - Identifikátor položky - Odstrániť dodatočný filter - URI položky - Nový uzol - Uveďte názov uzlu, ktorý chcete pridať - Zoznam uzlov reprezentujúce cieľovú URI - Pridať - Povoliť export? - - - Ikonevorschau - Ikone ID - Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar. - Zu zulässigen Rollen hinzufügen - Aus zulässigen Rollen entfernen - Zu verbotenen Rollen hinzufügen - Aus verbotenen Rollen entfernen - Ihre Prozesse - Wählen Sie einen Prozess mit Rollen aus, die Sie zu Listen mit zulässigen oder verbotenen Rollen hinzufügen möchten. - Verfügbare Rollen - Schaltflächentitel "Neuer Fall" - Ikone ID - Ikonevorschau - Anzuzeigende Attributmenge auswählen - Neue Filter auswählen - Beispiel: "meta-title,meta-visualId" - Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar. - Beispiel: "demo-tabbed-views" - Versteckt - Einfacher Suchmodus - Sortieren - Suchen - Bearbeiten - Kopfzeilenmodus - Standardkopfzeilenmodus - Erlaube Änderung des Kopfzeilenmodus? - Erlaube Tabellenmodus? - Eigene Kopfzeilen verwenden? - Anzuzeigende Attributmenge auswählen - Beispiel: "meta-title,meta-user" - Rollen - Filter - Zusätzlicher Filter - Aktueller Filter - Zulässige Rollen - Allgemein - Identifikationsnummer des Menüeintrages - Zusatzfilter entfernen - Menüeintrag-URI - Neuer Knoten - Hinzufügen - Ziel URI - Titel der kopierten Ansicht - Identifikator der kopierten Ansicht - Muss einzigartig sein - Titel des Eintrages - Wird im Menü angezeigt - Ikonen Identifikator der Registerkarte - Zeige die Registerkarte Ikone an? - Titel der Registerkarte - Wird in der Registerkarte angezeigt - Aktualisiere die Ansicht mit dem ausgewählten Filter - Eigener Ansicht anwenden? - Konfigurationsidentifikator der eigenen Ansicht - Suchmodus im Fallansicht - Einfacher und erweiterter Suchmodus - Erforde den Titel beim erzeugen von Fällen? - Ausgeschlossene Prozesse - Trenne die Prozessidentifikatoren mit einer Komma. z.B.: netz1,netz2 - Schaltfläche „Fall erstellen“ anzeigen? - "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen - Mit dem Basisfilter kombinieren? - Suchmodus im Aufgabenansicht - "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen - Menüeintrageinstellungen - Menüeintrag verschieben - verschieben - Menüeintrag duplizieren - duplizieren - Fallansicht - Aufgabenansicht - Rollen mit Zugriff auf diesen Menüeintrag - Verbotene Rollen - Rollen, für die wird den Menüeintrag ausgeblendet - Nächste URI-Teil angeben - Teile der Ziel URI - Erlaube Export? - - - - - initialize - 340 - 220 - - hourglass_empty - - admin - - true - true - true - true - - - - view - - filter_case - - forbidden - - filterCaseRef: f.filter_case, - menu_name: f.menu_name; - - if (filterCaseRef.value == null || filterCaseRef.value == []) { - return - } - - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - if (!menu_name.value) { - change menu_name value {return filterCase.dataSet["i18n_filter_name"].value} - } - - - - - - - - item_settings - 460 - 100 - - settings - auto - - admin - - true - true - true - true - - - - pre_general - 4 - grid - - menu_item_identifier - - visible - - - 0 - 0 - 1 - 2 - - outline - - - - nodePath - - visible - - - 2 - 0 - 1 - 2 - - outline - - - - - general_0 - 4 - grid - General - - menu_name - - editable - - - 0 - 0 - 1 - 2 - - outline - - - - menu_icon - - editable - - - 2 - 0 - 1 - 1 - - outline - - - - menu_icon_preview - - visible - - - 3 - 0 - 1 - 1 - - standard - - - - tab_name - - editable - - - 0 - 1 - 1 - 1 - - outline - - - - use_tab_icon - - editable - - - 1 - 1 - 1 - 1 - 0 - - - - 0 - - - trans: t.this, - iconPreview: f.tab_icon_preview, - icon: f.tab_icon, - useIcon: f.use_tab_icon; - - make iconPreview,visible on trans when { useIcon.value } - make icon,editable on trans when { useIcon.value } - - make iconPreview,hidden on trans when { !useIcon.value } - make icon,hidden on trans when { !useIcon.value } - - - - - - tab_icon - - editable - - - 2 - 1 - 1 - 1 - - outline - - - - tab_icon_preview - - visible - - - 3 - 1 - 1 - 1 - - standard - - - - use_custom_view - - editable - - - 0 - 2 - 1 - 1 - - outline - - - 0 - - - trans: t.this, - caseHeader: f.case_view_header, - caseTaskRef: f.case_view_settings_taskRef, - taskHeader: f.task_view_header, - taskTaskRef: f.task_view_settings_taskRef, - - useTabIcon: f.use_tab_icon, - tabIconPreview: f.tab_icon_preview, - tabName: f.tab_name, - tabIcon: f.tab_icon, - filterSelection: f.filter_autocomplete_selection, - updateFilter: f.update_filter, - selectedFilterPreview: f.selected_filter_preview, - currentFilterHeader: f.filter_header, - currentFilterPreview: f.current_filter_preview, - - use: f.use_custom_view, - selector: f.custom_view_selector; - - make selector,editable on trans when { use.value } - make selector,visible on trans when { !use.value } - - make caseHeader,visible on trans when { !use.value } - make caseHeader,hidden on trans when { use.value } - make caseTaskRef,editable on trans when { !use.value } - make caseTaskRef,hidden on trans when { use.value } - - make taskHeader,visible on trans when { !use.value } - make taskHeader,hidden on trans when { use.value } - make taskTaskRef,editable on trans when { !use.value } - make taskTaskRef,hidden on trans when { use.value } - - make useTabIcon,editable on trans when { !use.value } - make useTabIcon,hidden on trans when { use.value } - make tabIconPreview,visible on trans when { !use.value } - make tabIconPreview,hidden on trans when { use.value } - make tabName,editable on trans when { !use.value } - make tabName,hidden on trans when { use.value } - make tabIcon,editable on trans when { !use.value } - make tabIcon,hidden on trans when { use.value } - make filterSelection,editable on trans when { !use.value } - make filterSelection,hidden on trans when { use.value } - make updateFilter,visible on trans when { !use.value } - make updateFilter,hidden on trans when { use.value } - make selectedFilterPreview,visible on trans when { !use.value } - make selectedFilterPreview,hidden on trans when { use.value } - make currentFilterHeader,visible on trans when { !use.value } - make currentFilterHeader,hidden on trans when { use.value } - make currentFilterPreview,visible on trans when { !use.value } - make currentFilterPreview,hidden on trans when { use.value } - - - - - - custom_view_selector - - visible - - - 1 - 2 - 1 - 3 - - outline - - - - - roles_management - 5 - grid - Roles - - processes_available - - editable - - - 0 - 0 - 2 - 1 - 0 - - outline - - - - roles_available - - editable - - - 1 - 0 - 2 - 1 - 0 - - outline - - - - add_allowed_roles - - editable - - - 2 - 0 - 1 - 1 - 0 - - - - - allowed_roles - - editable - - - 3 - 0 - 1 - 1 - 0 - - outline - - - - remove_allowed_roles - - editable - - - 4 - 0 - 1 - 1 - 0 - - - - - add_banned_roles - - editable - - - 2 - 1 - 1 - 1 - 0 - - - - - banned_roles - - editable - - - 3 - 1 - 1 - 1 - 0 - - outline - - - - remove_banned_roles - - editable - - - 4 - 1 - 1 - 1 - 0 - - - - - - filter_update - 4 - grid - Filter - - filter_autocomplete_selection - - editable - - - 0 - 0 - 1 - 3 - - outline - - - - update_filter - - visible - - - 3 - 0 - 1 - 1 - - standard - - - - selected_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - - current_filter - 4 - grid - - filter_header - - visible - - - 0 - 0 - 1 - 4 - - outline - - - - current_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - - case_view_settings_dataGroup - 4 - grid - - case_view_header - - hidden - - - 0 - 0 - 1 - 4 - - outline - - - - case_view_settings_taskRef - - hidden - - - 0 - 1 - 1 - 4 - - outline - - - - - task_view_settings_dataGroup - 4 - grid - - task_view_header - - hidden - - - 0 - 0 - 1 - 4 - - outline - - - - task_view_settings_taskRef - - hidden - - - 0 - 1 - 1 - 4 - - outline - - - - - - - move_item - 580 - 100 - - move_down - auto - - admin - - true - true - true - true - - - - move - 4 - grid - - move_dest_uri - - editable - required - - - 0 - 0 - 1 - 2 - - outline - - - - move_dest_uri_new_node - - editable - - - 2 - 0 - 1 - 1 - - outline - - - - move_add_node - - editable - - - 3 - 0 - 1 - 1 - - outline - - - - - finish - - - dest: f.move_dest_uri; - - if (dest.value == null || dest.value == []) { - throw new IllegalArgumentException("URI must not be empty!") - } - - String newUri = dest.value.join("/") - newUri = newUri.replace("//","/") - - changeMenuItem useCase uri { newUri } - - - Move - - - - - duplicate_item - 580 - 340 - - content_copy - auto - - admin - - true - true - true - true - - - - duplicate - 4 - grid - - duplicate_new_title - - editable - required - - - 0 - 0 - 1 - 4 - - outline - - - - duplicate_view_identifier - - editable - required - - - 0 - 1 - 1 - 4 - - outline - - - - - finish - - - identifier: f.duplicate_view_identifier, - title: f.duplicate_new_title; - - duplicateMenuItem(useCase, title.value, identifier.value) - - - Duplicate - - - - - change_filter - 460 - 340 - - - system - - true - - - - new_filter_id - - editable - required - - - set_event_0 - - - new_filter_id: f.new_filter_id, - filterTaskRef: f.current_filter_preview, - filterCaseRef: f.filter_case; - - change filterCaseRef value { [new_filter_id.value] } - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - change filterTaskRef value {return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId]} - - - - - - - - case_view_settings - 340 - 100 - - - system - - true - - - - case_view_dataGroup - 4 - grid - - case_view_search_type - - editable - required - - - 0 - 0 - 1 - 1 - - outline - - - - case_allow_export - - editable - required - - - 1 - 0 - 1 - 1 - - outline - - - - show_create_case_button - - editable - required - - - 2 - 0 - 1 - 1 - - outline - - - - case_show_more_menu - - editable - required - - - 3 - 0 - 1 - 1 - - outline - - - - create_case_button_title - - editable - - - 0 - 1 - 1 - 1 - 0 - - outline - - - - create_case_button_icon - - editable - - - 1 - 1 - 1 - 1 - 0 - - outline - - - - create_case_button_icon_preview - - visible - - - 2 - 1 - 1 - 1 - 0 - - standard - - - - case_require_title_in_creation - - editable - - - 3 - 1 - 1 - 1 - 0 - - standard - - - - case_banned_nets_in_creation - - editable - - - 0 - 2 - 1 - 4 - 0 - - outline - - - - - case_view_headers - 5 - grid - - case_is_header_mode_changeable - - editable - required - - trans: t.this, - isChangeable: f.case_is_header_mode_changeable, - mode: f.case_headers_mode, - defaultMode: f.case_headers_default_mode; - - make mode,editable on trans when { isChangeable.value } - make mode,required on trans when { isChangeable.value } - make defaultMode,editable on trans when { isChangeable.value } - make defaultMode,required on trans when { isChangeable.value } - - make mode,hidden on trans when { !isChangeable.value } - make mode,optional on trans when { !isChangeable.value } - make defaultMode,hidden on trans when { !isChangeable.value } - make defaultMode,optional on trans when { !isChangeable.value } - - - - 0 - 0 - 1 - 1 - 0 - - outline - - - - case_allow_header_table_mode - - editable - required - - - 1 - 0 - 1 - 1 - 0 - - outline - - - - case_headers_mode - - editable - required - - - 2 - 0 - 1 - 2 - 0 - - outline - - - - case_headers_default_mode - - editable - required - - - 4 - 0 - 1 - 1 - 0 - - outline - - - - use_case_default_headers - - editable - - trans: t.this, - use: f.use_case_default_headers, - headers: f.case_default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - - - - 0 - 1 - 1 - 1 - 0 - - outline - - - - case_default_headers - - editable - - - 1 - 1 - 1 - 4 - 0 - - outline - - - - - - - task_view_settings - 340 - 340 - - - system - - true - - - - task_view_dataGroup - 4 - grid - - task_view_search_type - - editable - required - - - 0 - 0 - 1 - 3 - - outline - - - - task_show_more_menu - - editable - required - - - 3 - 0 - 1 - 1 - - outline - - - - - task_view_headers - 5 - grid - - task_is_header_mode_changeable - - editable - required - - trans: t.this, - isChangeable: f.task_is_header_mode_changeable, - mode: f.task_headers_mode, - defaultMode: f.task_headers_default_mode; - - make mode,editable on trans when { isChangeable.value } - make mode,required on trans when { isChangeable.value } - make defaultMode,editable on trans when { isChangeable.value } - make defaultMode,required on trans when { isChangeable.value } - - make mode,hidden on trans when { !isChangeable.value } - make mode,optional on trans when { !isChangeable.value } - make defaultMode,hidden on trans when { !isChangeable.value } - make defaultMode,optional on trans when { !isChangeable.value } - - - - 0 - 0 - 1 - 1 - 0 - - outline - - - - task_allow_header_table_mode - - editable - required - - - 1 - 0 - 1 - 1 - - outline - - - - task_headers_mode - - editable - required - - - 2 - 0 - 1 - 2 - - outline - - - - task_headers_default_mode - - editable - required - - - 4 - 0 - 1 - 1 - - outline - - - - use_task_default_headers - - editable - - trans: t.this, - use: f.use_task_default_headers, - headers: f.task_default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - - - - 0 - 1 - 1 - 1 - 0 - - outline - - - - task_default_headers - - editable - - - 1 - 1 - 1 - 4 - 0 - - outline - - - - - additional_filter_update - 4 - grid - Additional filter - - additional_filter_autocomplete_selection - - editable - - - 0 - 0 - 1 - 3 - - outline - - - - update_additional_filter - - editable - - - 3 - 0 - 1 - 1 - - standard - - - - selected_additional_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - - current_additional_filter - 4 - grid - - filter_header - - hidden - - - 0 - 0 - 1 - 4 - - outline - - - - current_additional_filter_preview - - visible - - - 0 - 1 - 1 - 4 - - standard - - - - merge_filters - - hidden - - - 0 - 2 - 1 - 1 - - standard - - - - remove_additional_filter - - hidden - - - 1 - 2 - 1 - 1 - - standard - - - - - - children_order - 580 - 220 - - low_priority - auto - - admin - - true - true - true - true - - - - children_order_0 - 4 - grid - - childItemForms - - editable - - forms: f.childItemForms, - ids: f.childItemIds; - - def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } - change forms value { orderedTaskIds } - - - - 0 - 0 - 1 - 4 - - outline - - - - - - row_for_ordering - 741 - 219 - - - system - - true - true - true - true - - - - row_for_ordering_0 - 6 - grid - - menu_item_identifier - - visible - - - 0 - 0 - 1 - 2 - - outline - - - - menu_name_as_visible - - visible - - - 2 - 0 - 1 - 2 - - outline - - - - order_down - - editable - - - 4 - 0 - 1 - 1 - 1 - - outline - - - - order_up - - editable - - - 5 - 0 - 1 - 1 - 1 - - outline - - - - - finish - - - - delegate - - - - - - - uninitialized - 220 - 220 - - 1 - false - - - initialized - 460 - 220 - - 0 - false - - - - - a1 - regular - uninitialized - initialize - 1 - - - a7 - read - initialized - item_settings - 1 - - - a8 - regular - initialize - initialized - 1 - - - a9 - read - initialized - move_item - 1 - - - a10 - read - initialized - duplicate_item - 1 - - - a12 - read - initialized - change_filter - 1 - - - a13 - read - initialized - children_order - 1 - - diff --git a/src/test/groovy/com/netgrif/application/engine/export/service/XlsExportServiceTest.java b/src/test/groovy/com/netgrif/application/engine/export/service/XlsExportServiceTest.java index 392d9b1f063..4abac8a4822 100644 --- a/src/test/groovy/com/netgrif/application/engine/export/service/XlsExportServiceTest.java +++ b/src/test/groovy/com/netgrif/application/engine/export/service/XlsExportServiceTest.java @@ -42,7 +42,7 @@ public class XlsExportServiceTest { void shouldCreateXlsxFile() throws Exception { LoggedUser superUser = superCreator.getSuperUser().transformToLoggedUser(); - IntStream.range(0,5).forEach(idx -> workflowService.createCaseByIdentifier(FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER, "Test case", "", superUser)); + IntStream.range(0,5).forEach(idx -> workflowService.createCaseByIdentifier(FilterRunner.MENU_NET_IDENTIFIER, "Test case", "", superUser)); FilteredCasesRequest request = getTestRequest(); File excel = xlsExportService.getExportFilteredCasesFile(request, superUser, Locale.ENGLISH); @@ -59,7 +59,7 @@ FilteredCasesRequest getTestRequest() { FilteredCasesRequest request = new FilteredCasesRequest(); request.setQuery(List.of( CaseSearchRequest.builder() - .query("processIdentifier:" + FilterRunner.PREFERRED_ITEM_NET_IDENTIFIER) + .query("processIdentifier:" + FilterRunner.MENU_NET_IDENTIFIER) .build())); request.setSelectedDataFieldNames(List.of("Menu Item Identifier", "Item URI", "Menu icon identifier", "Name of the item", "Tab icon identifier", "Name of the item")); request.setSelectedDataFieldIds(List.of("menu_item_identifier", "nodePath", "menu_icon", "menu_name", "tab_icon", "tab_name")); From 23769434503318e26b399dfb0db5ca3edfd97a87 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 4 May 2026 08:08:28 +0200 Subject: [PATCH 062/174] [ETASK-23] Dynamic view configuration - update text translations --- .../menu/domain/configurations/TabbedCaseViewBody.java | 5 +++-- .../petriNets/engine-processes/menu/menu_item.xml | 8 +++++--- .../menu/tabbed_case_view_configuration.xml | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index 5f18b2920cb..da2fb5314d6 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,7 +16,7 @@ @EqualsAndHashCode(callSuper = true) public class TabbedCaseViewBody extends ViewBody { private String viewSearchType = "fulltext_advanced"; - private String createCaseButtonTitle; + private I18nString createCaseButtonTitle; private String createCaseButtonIcon = "add"; private boolean requireTitleInCreation = true; private boolean showCreateCaseButton = true; @@ -45,7 +46,7 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.TEXT, + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, this.createCaseButtonTitle); outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, this.createCaseButtonIcon); diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 8fe4ae4c9f7..f9f9fc7335b 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -468,7 +468,7 @@ tab_icon_preview - Tab icon preview + Tab icon preview htmltextarea @@ -614,6 +614,7 @@ Bude zobrazený v menu Identifikátor ikony v karte Identifikátor Material ikony. Zoznam ikon s identifikátormi je dostupný online. + Náhľad ikony v karte Zobraziť ikonu v karte? Názov položky Bude zobrazený v karte @@ -670,6 +671,7 @@ Titel des Eintrages Wird im Menü angezeigt Ikonen Identifikator der Registerkarte + Vorschau der Registerkarte Ikone Zeige die Registerkarte Ikone an? Titel der Registerkarte Wird in der Registerkarte angezeigt @@ -1436,11 +1438,11 @@ finish - + </event> <event type="delegate"> <id>delegate</id> - <title> + </event> </transition> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 3e1adad7aa7..cdb1d1c0ecb 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -229,7 +229,7 @@ </options> <init>fulltext_advanced</init> </data> - <data type="text" immediate="true"> + <data type="i18n" immediate="true"> <id>create_case_button_title</id> <title name="create_case_button_title">"New case" button title From a94fafd4257539468f56259ae0dfc49267e056f7 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 4 May 2026 09:16:19 +0200 Subject: [PATCH 063/174] [ETASK-23] Dynamic view configuration - fix null node - add todo --- .../menu/domain/configurations/TabbedCaseViewBody.java | 6 ++++-- .../application/engine/menu/services/MenuItemService.java | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java index da2fb5314d6..661ff73eaa5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java @@ -46,8 +46,10 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, - this.createCaseButtonTitle); + if (this.createCaseButtonTitle != null) { + outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, + this.createCaseButtonTitle); + } outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, this.createCaseButtonIcon); outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index 23f18825609..eceb876213f 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -104,6 +104,8 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", sanitizedIdentifier)); } + // todo ETASK-23 validation + Case parentItemCase = getOrCreateFolderItem(body.getUri()); I18nString newName = body.getMenuName(); if (newName == null) { From 42b8bb3658979b3c60d698545fb7802fca34bcf2 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 4 May 2026 12:02:58 +0200 Subject: [PATCH 064/174] [ETASK-23] Dynamic view configuration - update petri net of menu_item.xml - fix handling of uri - update role permission in menu_item.xml --- .../engine/menu/domain/MenuItemConstants.java | 2 +- .../engine/menu/services/MenuItemService.java | 5 +- .../engine/menu/utils/MenuItemUtils.java | 2 +- .../engine-processes/menu/menu_item.xml | 146 +++++++++++++++--- 4 files changed, 130 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java index d6ac1a37afc..0c1912a69fc 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -30,6 +30,6 @@ public class MenuItemConstants { public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; public static final String TRANS_SETTINGS_ID = "item_settings"; - public static final String TRANS_INIT_ID = "initialize"; + public static final String TRANS_INIT_ID = "system_initialize"; public static final String TRANS_SYNC_ID = "data_sync"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java index eceb876213f..05ae30fd936 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java @@ -104,7 +104,7 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", sanitizedIdentifier)); } - // todo ETASK-23 validation + // todo 23 validation Case parentItemCase = getOrCreateFolderItem(body.getUri()); I18nString newName = body.getMenuName(); @@ -577,6 +577,9 @@ protected List updateNodeInChildrenFoldersRecursive(Case parentFolder) { protected void resolveAndHandleNewNodePath(Case folderItem, String destUri) { String newNodePath = resolveNewNodePath(folderItem, destUri); + if (newNodePath.startsWith("//")) { + newNodePath = newNodePath.replace("//", uriService.getUriSeparator()); + } UriNode newNode = uriService.getOrCreate(newNodePath, UriContentType.CASE); folderItem.getDataField(MenuItemConstants.FIELD_NODE_PATH).setValue(newNode.getUriPath()); } diff --git a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java index d1fcaf8e590..277de33d43a 100644 --- a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java +++ b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java @@ -64,7 +64,7 @@ public static String findTaskIdInCase(Case useCase, String transId) { * */ public static boolean isCyclicNodePath(Case folderItem, String destUri) { String oldNodePath = (String) folderItem.getFieldValue(MenuItemConstants.FIELD_NODE_PATH); - return destUri.contains(oldNodePath); + return oldNodePath != null && destUri.contains(oldNodePath); } /** diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index f9f9fc7335b..9f934d81974 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -25,8 +25,6 @@ default - false - false true @@ -643,6 +641,9 @@ Použiť zobrazenie v taboch? Vybrať zobrazenie Nastavenie zobrazenia + Vytvoriť menu položku + Zoradiť pod-položky + Vytvoriť menu položku Ikonevorschau @@ -692,6 +693,9 @@ Möchten Sie die Ansicht mit Registerkarten verwenden? Wählen Sie einen Ansichtstyp Die Ansichtskonfiguration + Menüeintrag erstellen + Untereintrage sortieren + Erstellen @@ -699,22 +703,96 @@ initialize 340 220 - + hourglass_empty admin true - true - true - true + + initialize_0 + 4 + grid + + menu_item_identifier + + editable + required + + + 0 + 0 + 1 + 4 + + outline + + + + move_dest_uri + + editable + required + + + 0 + 1 + 1 + 2 + + outline + + + + move_dest_uri_new_node + + editable + + + 2 + 1 + 1 + 1 + + outline + + + + move_add_node + + editable + + + 3 + 1 + 1 + 1 + + outline + + + + + finish + + + dest: f.move_dest_uri; + + String newUri = dest.value.join("/") + newUri = newUri.replace("//","/") + + changeMenuItem useCase uri { newUri } + + + Create + data_sync 340 - 340 + 112 system @@ -722,6 +800,12 @@ true + + admin + + true + + @@ -735,9 +819,12 @@ admin true + + + + default + true - true - true @@ -1178,9 +1265,6 @@ admin true - true - true - true @@ -1262,9 +1346,6 @@ admin true - true - true - true @@ -1320,16 +1401,13 @@ children_order 580 220 - + low_priority auto admin true - true - true - true @@ -1368,9 +1446,6 @@ system true - true - true - true @@ -1445,6 +1520,19 @@ </event> </transition> + <transition> + <id>system_initialize</id> + <x>336</x> + <y>304</y> + <label>System create</label> + <icon>computer</icon> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + </transition> <!-- PLACES--> <place> @@ -1507,4 +1595,18 @@ <destinationId>children_order</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a14</id> + <type>regular</type> + <sourceId>uninitialized</sourceId> + <destinationId>system_initialize</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a15</id> + <type>regular</type> + <sourceId>system_initialize</sourceId> + <destinationId>initialized</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file From b5164e4127c5b788102f772fc7197502331d20f6 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Wed, 6 May 2026 12:23:27 +0200 Subject: [PATCH 065/174] [ETASK-23] Dynamic view configuration - refactor menu item view forms - implement MenuController.getMenuItemData - implement MenuItemServiceTest.getMenuItemDataTest --- .../logic/action/ActionDelegate.groovy | 6 +- .../startup/DefaultDashboardRunner.groovy | 2 +- .../DashboardItemServiceImpl.java | 4 +- .../DashboardManagementServiceImpl.java | 4 +- .../MenuItemService.java | 48 +- .../interfaces/DashboardItemService.java | 2 +- .../DashboardManagementService.java | 2 +- .../interfaces/IMenuItemService.java | 5 +- .../engine/menu/utils/MenuItemUtils.java | 2 +- .../engine/menu/web/MenuController.java | 45 + .../responsebodies/MenuItemDataResponse.java | 23 + .../engine/workflow/domain/Case.java | 2 +- .../workflow/service/WorkflowService.java | 6 +- .../engine-processes/menu/menu_item.xml | 802 ++++++++++-------- .../menu/tabbed_case_view_configuration.xml | 457 +++++++--- .../tabbed_single_task_view_configuration.xml | 60 +- .../menu/tabbed_task_view_configuration.xml | 274 ++++-- .../menu/tabbed_ticket_view_configuration.xml | 101 ++- .../engine/menu/MenuItemServiceTest.java | 156 ++++ 19 files changed, 1388 insertions(+), 613 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/DashboardItemServiceImpl.java (97%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/DashboardManagementServiceImpl.java (98%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/MenuItemService.java (93%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/interfaces/DashboardItemService.java (88%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/interfaces/DashboardManagementService.java (89%) rename src/main/java/com/netgrif/application/engine/menu/{services => service}/interfaces/IMenuItemService.java (93%) create mode 100644 src/main/java/com/netgrif/application/engine/menu/web/MenuController.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java create mode 100644 src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 26791594b3e..d4b527dcb6c 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -31,9 +31,9 @@ import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewB import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody -import com.netgrif.application.engine.menu.services.interfaces.DashboardItemService -import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService -import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService +import com.netgrif.application.engine.menu.service.interfaces.DashboardItemService +import com.netgrif.application.engine.menu.service.interfaces.DashboardManagementService +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.pdf.generator.config.PdfResource import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator diff --git a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy index 7e5475ef098..1e580755668 100644 --- a/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy +++ b/src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy @@ -2,7 +2,7 @@ package com.netgrif.application.engine.startup import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody -import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService +import com.netgrif.application.engine.menu.service.interfaces.DashboardManagementService import com.netgrif.application.engine.petrinet.domain.I18nString import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java similarity index 97% rename from src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java rename to src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java index cd8b2c3a09c..20dbb8dbe18 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services; +package com.netgrif.application.engine.menu.service; import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; @@ -8,7 +8,7 @@ import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; -import com.netgrif.application.engine.menu.services.interfaces.DashboardItemService; +import com.netgrif.application.engine.menu.service.interfaces.DashboardItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java b/src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java similarity index 98% rename from src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java rename to src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java index c35ce0267f8..944270d33e5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services; +package com.netgrif.application.engine.menu.service; import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; @@ -9,7 +9,7 @@ import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemConstants; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementConstants; -import com.netgrif.application.engine.menu.services.interfaces.DashboardManagementService; +import com.netgrif.application.engine.menu.service.interfaces.DashboardManagementService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java similarity index 93% rename from src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java rename to src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 05ae30fd936..4c8f3d9ae65 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services; +package com.netgrif.application.engine.menu.service; import com.netgrif.application.engine.auth.domain.IUser; import com.netgrif.application.engine.auth.domain.LoggedUser; @@ -11,11 +11,12 @@ import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; import com.netgrif.application.engine.menu.domain.configurations.ViewBody; import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; -import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriContentType; import com.netgrif.application.engine.petrinet.domain.UriNode; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; @@ -23,6 +24,7 @@ import com.netgrif.application.engine.startup.FilterRunner; import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.DataField; import com.netgrif.application.engine.workflow.domain.Task; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; @@ -400,6 +402,48 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { return workflowService.save(parentFolder); } + /** + * Retrieves menu item data along with its associated view configuration data. The method collects immediate + * data from the menu item case and recursively traverses through all associated view configuration cases, + * aggregating their immediate data into a single map. + * + * @param caseId identifier of the menu item case + * @return map where keys are process identifiers (with "_configuration" suffix removed for view cases) and + * values are lists of immediate data fields from the corresponding cases + */ + @Override + public Map<String, List<Field<?>>> getMenuItemData(String caseId) { + Case menuItemCase = workflowService.findOne(caseId); + Map<String, List<Field<?>>> immediateDataMap = new HashMap<>(); + immediateDataMap.put(menuItemCase.getProcessIdentifier(), menuItemCase.getImmediateData()); + Optional<String> viewCaseIdOpt = getNextViewCaseId(menuItemCase); + if (viewCaseIdOpt.isEmpty()) { + return immediateDataMap; + } + + do { + Case viewCase = workflowService.findOne(viewCaseIdOpt.get()); + immediateDataMap.put(viewCase.getProcessIdentifier().replace("_configuration", ""), + viewCase.getImmediateData()); + viewCaseIdOpt = getNextViewCaseId(viewCase); + } while (viewCaseIdOpt.isPresent()); + + return immediateDataMap; + } + + protected Optional<String> getNextViewCaseId(Case parentCase) { + DataField viewConfigurationIdDataField = parentCase.getDataField(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID); + if (viewConfigurationIdDataField == null) { + return Optional.empty(); + } + @SuppressWarnings("unchecked") + List<String> viewConfigurationIdValue = (List<String>) viewConfigurationIdDataField.getValue(); + if (viewConfigurationIdValue == null || viewConfigurationIdValue.isEmpty()) { + return Optional.empty(); + } + return Optional.ofNullable(viewConfigurationIdValue.get(0)); + } + protected Case findCase(String processIdentifier, String query) { CaseSearchRequest request = CaseSearchRequest.builder() .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java similarity index 88% rename from src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java rename to src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java index 6555ad2c04f..3dd698d53c1 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services.interfaces; +package com.netgrif.application.engine.menu.service.interfaces; import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java similarity index 89% rename from src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java rename to src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java index 01efb79d055..e5765ddbf3b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/DashboardManagementService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java @@ -1,4 +1,4 @@ -package com.netgrif.application.engine.menu.services.interfaces; +package com.netgrif.application.engine.menu.service.interfaces; import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; diff --git a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java similarity index 93% rename from src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java rename to src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java index 9de2a7f2308..929f348cf10 100644 --- a/src/main/java/com/netgrif/application/engine/menu/services/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java @@ -1,14 +1,16 @@ -package com.netgrif.application.engine.menu.services.interfaces; +package com.netgrif.application.engine.menu.service.interfaces; import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriNode; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.petrinet.domain.dataset.MapOptionsField; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -27,6 +29,7 @@ public interface IMenuItemService { void moveItem(Case item, String destUri) throws TransitionNotExecutableException; Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; Case removeChildItemFromParent(String folderId, Case childItem); + Map<String, List<Field<?>>> getMenuItemData(String caseId); /** * Gets all tabbed or non-tabbed views diff --git a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java index 277de33d43a..8472e838599 100644 --- a/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java +++ b/src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java @@ -3,7 +3,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemConstants; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.workflow.domain.TaskPair; -import com.netgrif.application.engine.menu.services.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import java.text.Normalizer; import java.util.List; diff --git a/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java new file mode 100644 index 00000000000..27ffee572e9 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java @@ -0,0 +1,45 @@ +package com.netgrif.application.engine.menu.web; + +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; +import com.netgrif.application.engine.menu.web.responsebodies.MenuItemDataResponse; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.MediaTypes; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Base64; +import java.util.List; +import java.util.Map; + +@Slf4j +@RestController +@Tag(name = "Menu") +@RequiredArgsConstructor +@RequestMapping("/api/menu") +public class MenuController { + + private final IMenuItemService menuItemService; + + @Operation(summary = "Get relevant data for the menu item", security = {@SecurityRequirement(name = "BasicAuth")}) + @GetMapping(value = "/{encodedCaseId}", produces = MediaTypes.HAL_JSON_VALUE) + public EntityModel<MenuItemDataResponse> getMenuItemData(String encodedCaseId) { + try { + String caseId = new String(Base64.getDecoder().decode(encodedCaseId)); + Map<String, List<Field<?>>> immediateDataMap = menuItemService.getMenuItemData(caseId); + return EntityModel.of(new MenuItemDataResponse(immediateDataMap)); + } catch (Exception e) { + log.error("Getting menu item data failed", e); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Getting menu item data failed", e); + } + } +} + diff --git a/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java new file mode 100644 index 00000000000..43f265a9547 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java @@ -0,0 +1,23 @@ +package com.netgrif.application.engine.menu.web.responsebodies; + +import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequiredArgsConstructor +public class MenuItemDataResponse { + + /** + * Map containing menu item data where key is the view type and value is a list of immediate fields. + */ + @Getter + private final Map<String, List<Field<?>>> data; + + public MenuItemDataResponse() { + this(new HashMap<>()); + } +} diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java index 1559cbe0017..cd0c8f24596 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java @@ -100,7 +100,7 @@ public class Case implements Serializable { @Getter @Setter @Transient - private List<Field> immediateData; + private List<Field<?>> immediateData; @Getter @Setter diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index 472df01d5ae..6cd2c5e8826 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -505,11 +505,11 @@ public List<Field> getData(String caseId) { } private void setImmediateDataFieldsReadOnly(Case useCase) { - List<Field> immediateData = new ArrayList<>(); + List<Field<?>> immediateData = new ArrayList<>(); useCase.getImmediateDataFields().forEach(fieldId -> { try { - Field clone = fieldFactory.buildImmediateField(useCase, fieldId); + Field<?> clone = fieldFactory.buildImmediateField(useCase, fieldId); immediateData.add(clone); } catch (Exception e) { log.error("Could not built immediate field [" + fieldId + "]"); @@ -526,7 +526,7 @@ protected Page<Case> setImmediateDataFields(Page<Case> cases) { } protected Case setImmediateDataFields(Case useCase) { - List<Field> immediateData = new ArrayList<>(); + List<Field<?>> immediateData = new ArrayList<>(); useCase.getImmediateDataFields().forEach(fieldId -> immediateData.add(fieldFactory.buildImmediateField(useCase, fieldId)) diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 9f934d81974..1f53c563974 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -591,6 +591,31 @@ ]) </action> </data> + <data type="i18n"> + <id>role_divider</id> + <title/> + <component> + <name>divider</name> + </component> + </data> + <data type="taskRef"> + <id>advanced_content_form</id> + <title/> + <component> + <name>task-list</name> + <!-- todo 23 headers--> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "role_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "view_settings"}.task] } + </action> + </actions> + </event> + </data> <!-- I18NS --> <i18n locale="sk"> @@ -629,7 +654,6 @@ <i18nString name="roles_allowed_desc">Zoznam povolených rolí, ktoré môžu zobraziť túto položku</i18nString> <i18nString name="roles_banned">Zakázané roly</i18nString> <i18nString name="roles_banned_desc">Zoznam zakázaných rolí, ktoré nemôžu zobraziť túto položku</i18nString> - <i18nString name="item_settings_general">Všeobecné</i18nString> <i18nString name="menu_item_identifier">Identifikátor položky</i18nString> <i18nString name="nodePath">URI položky</i18nString> <i18nString name="move_dest_uri_new_node">Nový uzol</i18nString> @@ -660,7 +684,6 @@ <i18nString name="custom_view_selector_desc">Beispiel: "demo-tabbed-views"</i18nString> <i18nString name="roles_management_title">Rollen</i18nString> <i18nString name="roles_allowed">Zulässige Rollen</i18nString> - <i18nString name="item_settings_general">Allgemein</i18nString> <i18nString name="menu_item_identifier">Identifikationsnummer des Menüeintrages</i18nString> <i18nString name="nodePath">Menüeintrag-URI</i18nString> <i18nString name="move_dest_uri_new_node">Neuer Knoten</i18nString> @@ -800,12 +823,6 @@ <perform>true</perform> </logic> </roleRef> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> </transition> <transition> @@ -831,7 +848,6 @@ <id>general_0</id> <cols>4</cols> <layout>grid</layout> - <title name="item_settings_general">General menu_item_identifier @@ -883,7 +899,7 @@ 2 1 1 - 1 + 2 outline @@ -894,10 +910,10 @@ visible - 3 - 1 + 2 + 2 1 - 1 + 2 standard @@ -908,52 +924,73 @@ editable - 1 + 0 2 1 - 1 + 2 standard - - - roles_management - 5 - grid - Roles - processes_available + advanced_content_form editable 0 - 0 - 2 - 1 - 0 + 3 + 1 + 4 outline + + + assign_0 + + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + + <transition> + <id>move_item</id> + <x>580</x> + <y>100</y> + <label name="move_item">Move item</label> + <icon>move_down</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <dataGroup> + <id>move</id> + <cols>4</cols> + <layout>grid</layout> <dataRef> - <id>roles_available</id> + <id>move_dest_uri</id> <logic> <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> - <x>1</x> + <x>0</x> <y>0</y> - <rows>2</rows> - <cols>1</cols> - <offset>0</offset> + <rows>1</rows> + <cols>2</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>add_allowed_roles</id> + <id>move_dest_uri_new_node</id> <logic> <behavior>editable</behavior> </logic> @@ -962,12 +999,12 @@ <y>0</y> <rows>1</rows> <cols>1</cols> - <offset>0</offset> <template>material</template> + <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>allowed_roles</id> + <id>move_add_node</id> <logic> <behavior>editable</behavior> </logic> @@ -976,90 +1013,439 @@ <y>0</y> <rows>1</rows> <cols>1</cols> - <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> + </dataGroup> + <event type="finish"> + <id>finish</id> + <actions phase="pre"> + <action> + dest: f.move_dest_uri; + + if (dest.value == null || dest.value == []) { + throw new IllegalArgumentException("URI must not be empty!") + } + + String newUri = dest.value.join("/") + newUri = newUri.replace("//","/") + + changeMenuItem useCase uri { newUri } + </action> + </actions> + <title name="move_item_finish">Move + + + + + duplicate_item + 580 + 340 + + content_copy + auto + + admin + + true + + + + duplicate + 4 + grid - remove_allowed_roles + duplicate_new_title editable + required - 4 + 0 0 1 - 1 - 0 + 4 + outline - add_banned_roles + duplicate_view_identifier editable + required - 2 + 0 1 1 - 1 - 0 + 4 + outline + + + finish + + + identifier: f.duplicate_view_identifier, + title: f.duplicate_new_title; + + duplicateMenuItem(useCase, title.value, identifier.value) + + + Duplicate + + + + + children_order + 580 + 220 + + low_priority + auto + + admin + + true + + + + children_order_0 + 4 + grid - banned_roles + childItemForms editable + + forms: f.childItemForms, + ids: f.childItemIds; + + def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } + change forms value { orderedTaskIds } + - 3 - 1 + 0 + 0 1 - 1 - 0 + 4 + + outline + + + + + + row_for_ordering + 741 + 219 + + + system + + true + + + + row_for_ordering_0 + 6 + grid + + menu_item_identifier + + visible + + + 0 + 0 + 1 + 2 outline - remove_banned_roles + menu_name_as_visible + + visible + + + 2 + 0 + 1 + 2 + + outline + + + + order_down editable 4 - 1 + 0 1 1 - 0 + 1 + outline - - - configuration_view - 4 - grid - View configuration - use_custom_view + order_up editable - 0 + 5 0 1 1 + 1 outline - - 0 - + + + + finish + + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>system_initialize</id> + <x>336</x> + <y>304</y> + <label>System create</label> + <icon>computer</icon> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + </transition> + <transition> + <id>role_settings</id> + <x>400</x> + <y>48</y> + <label name="todo 23">Role</label> + <icon>person</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>roles_management</id> + <cols>3</cols> + <layout>grid</layout> + <dataRef> + <id>processes_available</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>2</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>roles_available</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>1</x> + <y>0</y> + <rows>2</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>add_allowed_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>add_banned_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>1</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>role_divider</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>2</y> + <rows>1</rows> + <cols>3</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>allowed_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>3</y> + <rows>1</rows> + <cols>2</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>remove_allowed_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>3</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + <dataRef> + <id>banned_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>4</y> + <rows>1</rows> + <cols>2</cols> + <offset>0</offset> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>remove_banned_roles</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>4</y> + <rows>1</rows> + <cols>1</cols> + <offset>0</offset> + <template>material</template> + </layout> + </dataRef> + </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>view_settings</id> + <x>528</x> + <y>48</y> + <label name="todo 23">View</label> + <icon>image</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>configuration_view</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>use_custom_view</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + <event type="set"> + <id>0</id> + <actions phase="post"> <action> trans: t.this, useTabIcon: f.use_tab_icon, @@ -1252,267 +1638,16 @@ </layout> </dataRef> </dataGroup> - </transition> - - <transition> - <id>move_item</id> - <x>580</x> - <y>100</y> - <label name="move_item">Move item</label> - <icon>move_down</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>move</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>move_dest_uri</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>move_dest_uri_new_node</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>2</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>move_add_node</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>3</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <event type="finish"> - <id>finish</id> - <actions phase="pre"> - <action> - dest: f.move_dest_uri; - - if (dest.value == null || dest.value == []) { - throw new IllegalArgumentException("URI must not be empty!") - } - - String newUri = dest.value.join("/") - newUri = newUri.replace("//","/") - - changeMenuItem useCase uri { newUri } - </action> - </actions> - <title name="move_item_finish">Move + + assign + </event> - </transition> - - <transition> - <id>duplicate_item</id> - <x>580</x> - <y>340</y> - <label name="duplicate_item">Duplicate item</label> - <icon>content_copy</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>duplicate</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>duplicate_new_title</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>duplicate_view_identifier</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>0</x> - <y>1</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <event type="finish"> - <id>finish</id> - <actions phase="pre"> - <action> - identifier: f.duplicate_view_identifier, - title: f.duplicate_new_title; - - duplicateMenuItem(useCase, title.value, identifier.value) - </action> - </actions> - <title name="duplicate_item_finish">Duplicate + + cancel + </event> - </transition> - - <transition> - <id>children_order</id> - <x>580</x> - <y>220</y> - <label name="children_order">Manage item order</label> - <icon>low_priority</icon> - <assignPolicy>auto</assignPolicy> - <roleRef> - <id>admin</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>children_order_0</id> - <cols>4</cols> - <layout>grid</layout> - <dataRef> - <id>childItemForms</id> - <logic> - <behavior>editable</behavior> - <action trigger="get"> - forms: f.childItemForms, - ids: f.childItemIds; - - def orderedTaskIds = ids.value?.collect { id -> workflowService.findOne(id).tasks.find { it.transition == "row_for_ordering" }.task } - change forms value { orderedTaskIds } - </action> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - </transition> - <transition> - <id>row_for_ordering</id> - <x>741</x> - <y>219</y> - <label>Row for ordering [referenced]</label> - <roleRef> - <id>system</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - <dataGroup> - <id>row_for_ordering_0</id> - <cols>6</cols> - <layout>grid</layout> - <dataRef> - <id>menu_item_identifier</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>menu_name_as_visible</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>2</x> - <y>0</y> - <rows>1</rows> - <cols>2</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>order_down</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>4</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <offset>1</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>order_up</id> - <logic> - <behavior>editable</behavior> - </logic> - <layout> - <x>5</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <offset>1</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> <event type="finish"> - <id>finish</id> + <id>finish_0</id> <title/> </event> <event type="delegate"> @@ -1520,19 +1655,6 @@ <title/> </event> </transition> - <transition> - <id>system_initialize</id> - <x>336</x> - <y>304</y> - <label>System create</label> - <icon>computer</icon> - <roleRef> - <id>system</id> - <logic> - <perform>true</perform> - </logic> - </roleRef> - </transition> <!-- PLACES--> <place> @@ -1609,4 +1731,18 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a16</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>view_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a17</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>role_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index cdb1d1c0ecb..39740f21fce 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -147,7 +145,7 @@ <name>autocomplete_dynamic</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -156,7 +154,7 @@ updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) </action> <action trigger="get"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -173,7 +171,7 @@ <name>raised</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, update_filter: f.update_filter, contains_filter: f.contains_filter, filter_case: f.filter_case, @@ -392,6 +390,26 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>advanced_content_form</id> + <title/> + <component> + <!-- todo 23 headers--> + <name>task-list</name> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "create_case_btn_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] } + </action> + </actions> + </event> + </data> <!-- I18NS --> <i18n locale="sk"> @@ -420,14 +438,12 @@ <i18nString name="allow_header_table_mode">Povoliť tabuľkový mód pre hlavičky?</i18nString> <i18nString name="use_default_headers">Použiť vlastné predvolené hlavičky?</i18nString> <i18nString name="item_settings">Nastavenie položky</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Súčasný filter</i18nString> <i18nString name="view_header">Zobrazenie prípadov</i18nString> <i18nString name="item_settings_general">Všeobecné</i18nString> <i18nString name="use_case_default_headers">Použiť predvolené hlavičky</i18nString> <i18nString name="view_configuration_type">Vybrať zobrazenie</i18nString> <i18nString name="settings">Nastavenie</i18nString> - <i18nString name="associated_view">Asociované zobrazenie</i18nString> </i18n> <i18n locale="de"> <i18nString name="create_case_button_title">Schaltflächentitel "Neuer Fall"</i18nString> @@ -446,7 +462,6 @@ <i18nString name="is_header_mode_changeable">Erlaube Änderung des Kopfzeilenmodus?</i18nString> <i18nString name="allow_header_table_mode">Erlaube Tabellenmodus?</i18nString> <i18nString name="use_default_headers">Eigene Kopfzeilen verwenden?</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Aktueller Filter</i18nString> <i18nString name="item_settings_general">Allgemein</i18nString> <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> @@ -462,7 +477,6 @@ <i18nString name="use_case_default_headers">Benutzerdefinierte Standardheader verwenden?</i18nString> <i18nString name="view_configuration_type">Wählen Sie einen Ansichtstyp</i18nString> <i18nString name="settings">Einstellungen</i18nString> - <i18nString name="associated_view">zugehörige Ansicht</i18nString> </i18n> <transition> @@ -471,12 +485,24 @@ <y>208</y> <label>initialize [await sync]</label> <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>data_sync</id> <x>368</x> <y>328</y> <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>settings</id> @@ -488,35 +514,94 @@ <id>admin</id> <logic> <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> <view>true</view> - <cancel>true</cancel> - <assign>true</assign> </logic> </roleRef> <dataGroup> - <id>form_title</id> + <id>view_dataGroup</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_header</id> + <id>view_search_type</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>2</x> + <y>0</y> + <rows>1</rows> + <cols>2</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>advanced_content_form</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> <cols>4</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign_0</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + <transition> + <id>filter_settings</id> + <x>432</x> + <y>48</y> + <label name="todo 23">Filter</label> + <icon>filter_alt</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> - <id>filter_update</id> + <id>filter_0</id> <cols>4</cols> <layout>grid</layout> - <title name="filter_update_title">Filter filter_autocomplete_selection @@ -588,48 +673,151 @@ + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>next_view_settings</id> + <x>560</x> + <y>48</y> + <label name="todo 23">Next view</label> + <icon>queue_play_next</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> - <id>view_dataGroup</id> + <id>associated_view_0</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_search_type</id> + <id>view_configuration_type</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> - <cols>2</cols> + <cols>4</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> + <event type="set"> + <id>0</id> + <actions phase="post"> + <action> + view_configuration_type: f.view_configuration_type, + view_configuration_form: f.view_configuration_form, + view_configuration_id: f.view_configuration_id; + + if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { + workflowService.deleteCase(view_configuration_id.value[0]) + } + + if (view_configuration_type.value == null || view_configuration_type.value == "") { + change view_configuration_id value { [] } + change view_configuration_form value { [] } + return + } + + def configurationCase = createCase(view_configuration_type.value + "_configuration") + def initTask = assignTask("initialize", configurationCase) + finishTask(initTask) + configurationCase = workflowService.findOne(configurationCase.stringId) + change view_configuration_id value { [configurationCase.stringId] } + change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + </action> + </actions> + </event> </dataRef> <dataRef> - <id>show_create_case_button</id> + <id>view_configuration_form</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> - <x>2</x> - <y>0</y> + <x>0</x> + <y>1</y> <rows>1</rows> - <cols>1</cols> + <cols>4</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> + </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>create_case_btn_settings</id> + <x>656</x> + <y>48</y> + <label name="todo 23">Create case button</label> + <icon>add_box</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>create_case_btn_0</id> + <cols>3</cols> + <layout>grid</layout> <dataRef> - <id>show_more_menu</id> + <id>show_create_case_button</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> - <x>3</x> + <x>0</x> <y>0</y> <rows>1</rows> <cols>1</cols> @@ -638,27 +826,27 @@ </layout> </dataRef> <dataRef> - <id>create_case_button_title</id> + <id>require_title_in_creation</id> <logic> <behavior>editable</behavior> </logic> <layout> - <x>0</x> - <y>1</y> + <x>1</x> + <y>0</y> <rows>1</rows> <cols>1</cols> <offset>0</offset> <template>material</template> - <appearance>outline</appearance> + <appearance>standard</appearance> </layout> </dataRef> <dataRef> - <id>create_case_button_icon</id> + <id>create_case_button_title</id> <logic> <behavior>editable</behavior> </logic> <layout> - <x>1</x> + <x>0</x> <y>1</y> <rows>1</rows> <cols>1</cols> @@ -668,27 +856,27 @@ </layout> </dataRef> <dataRef> - <id>create_case_button_icon_preview</id> + <id>create_case_button_icon</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> </logic> <layout> - <x>2</x> + <x>1</x> <y>1</y> <rows>1</rows> <cols>1</cols> <offset>0</offset> <template>material</template> - <appearance>standard</appearance> + <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>require_title_in_creation</id> + <id>create_case_button_icon_preview</id> <logic> - <behavior>editable</behavior> + <behavior>visible</behavior> </logic> <layout> - <x>3</x> + <x>2</x> <y>1</y> <rows>1</rows> <cols>1</cols> @@ -706,16 +894,52 @@ <x>0</x> <y>2</y> <rows>1</rows> - <cols>4</cols> + <cols>3</cols> <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>header_settings</id> + <x>336</x> + <y>48</y> + <label name="todo 23">Header</label> + <icon>view_column</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> - <id>view_headers</id> - <cols>5</cols> + <id>header_0</id> + <cols>3</cols> <layout>grid</layout> <dataRef> <id>is_header_mode_changeable</id> @@ -746,10 +970,17 @@ </layout> </dataRef> <dataRef> - <id>allow_header_table_mode</id> + <id>use_case_default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> + <action trigger="set"> + trans: t.this, + use: f.use_case_default_headers, + headers: f.default_headers; + + make headers,editable on trans when { use.value } + make headers,visible on trans when { !use.value } + </action> </logic> <layout> <x>1</x> @@ -762,31 +993,14 @@ </layout> </dataRef> <dataRef> - <id>headers_mode</id> + <id>default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> <x>2</x> <y>0</y> <rows>1</rows> - <cols>2</cols> - <offset>0</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>headers_default_mode</id> - <logic> - <behavior>editable</behavior> - <behavior>required</behavior> - </logic> - <layout> - <x>4</x> - <y>0</y> - <rows>1</rows> <cols>1</cols> <offset>0</offset> <template>material</template> @@ -794,17 +1008,10 @@ </layout> </dataRef> <dataRef> - <id>use_case_default_headers</id> + <id>allow_header_table_mode</id> <logic> <behavior>editable</behavior> - <action trigger="set"> - trans: t.this, - use: f.use_case_default_headers, - headers: f.default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - </action> + <behavior>required</behavior> </logic> <layout> <x>0</x> @@ -817,84 +1024,54 @@ </layout> </dataRef> <dataRef> - <id>default_headers</id> + <id>headers_mode</id> <logic> <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> <x>1</x> <y>1</y> <rows>1</rows> - <cols>4</cols> - <offset>0</offset> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - </dataGroup> - <dataGroup> - <id>associated_view</id> - <cols>4</cols> - <layout>grid</layout> - <title name="associated_view">Associated view - - view_configuration_type - - editable - - - 0 - 0 - 1 - 4 + 1 0 outline - - 0 - - - view_configuration_type: f.view_configuration_type, - view_configuration_form: f.view_configuration_form, - view_configuration_id: f.view_configuration_id; - - if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { - workflowService.deleteCase(view_configuration_id.value[0]) - } - - if (view_configuration_type.value == null || view_configuration_type.value == "") { - change view_configuration_id value { [] } - change view_configuration_form value { [] } - return - } - - def configurationCase = createCase(view_configuration_type.value + "_configuration") - def initTask = assignTask("initialize", configurationCase) - finishTask(initTask) - configurationCase = workflowService.findOne(configurationCase.stringId) - change view_configuration_id value { [configurationCase.stringId] } - change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } - - - - view_configuration_form + headers_default_mode editable + required - 0 + 2 1 1 - 4 + 1 0 outline + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> </transition> <place> <id>initialized</id> @@ -933,4 +1110,32 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>filter_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a5</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>next_view_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a6</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>header_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a7</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>create_case_btn_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 10c34b150e8..22f4853e17f 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -171,14 +169,6 @@ </data> <!-- VIEW CONFIGURATION DATA --> - <data type="i18n"> - <id>view_header</id> - <title/> - <init name="view_header">Single task view</init> - <component> - <name>divider</name> - </component> - </data> <data type="text"> <id>transition_id</id> <title name="transition_id">Transition id @@ -193,8 +183,6 @@ Súčasný filter Vybrať zobrazenie Nastavenie - Asociované zobrazenie - Zobrazenie jednej úlohy ID prechodu @@ -204,8 +192,6 @@ Aktualisiere die Ansicht mit dem ausgewählten Filter Wählen Sie einen Ansichtstyp Einstellungen - zugehörige Ansicht - Einzelaufgabenansicht Übergangs-ID @@ -215,12 +201,24 @@ 208 hourglass_empty + + admin + + true + + data_sync 368 328 + + system + + true + + settings @@ -232,30 +230,14 @@ admin true + + + + default + true - true - true - - form_title - 4 - grid - - view_header - - visible - - - 0 - 0 - 1 - 4 - - outline - - - view_dataGroup 4 @@ -275,6 +257,14 @@ + + assign_0 + + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> </transition> <place> <id>initialized</id> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index 66caae1268f..abc71d160a9 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -125,7 +123,7 @@ <name>autocomplete_dynamic</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -134,7 +132,7 @@ updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) </action> <action trigger="get"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -151,7 +149,7 @@ <name>raised</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, update_filter: f.update_filter, contains_filter: f.contains_filter, filter_case: f.filter_case, @@ -175,7 +173,7 @@ <id>filter_case</id> <title/> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterHeader: f.filter_header, removeButton: f.remove_filter, contains_filter: f.contains_filter, @@ -224,14 +222,6 @@ </data> <!-- TASK VIEW CONFIGURATION DATA --> - <data type="i18n"> - <id>view_header</id> - <title/> - <init name="view_header">Task view</init> - <component> - <name>divider</name> - </component> - </data> <data type="enumeration_map" immediate="true"> <id>view_search_type</id> <title name="view_search_type">Search type for task view @@ -315,6 +305,24 @@ Show more menu for task item? true + + advanced_content_form + + <component> + <!-- todo 23 headers--> + <name>task-list</name> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task] } + </action> + </actions> + </event> + </data> <!-- I18NS --> <i18n locale="sk"> @@ -337,9 +345,7 @@ <i18nString name="default_headers_desc">Napríklad: "meta-title,meta-user"</i18nString> <i18nString name="show_more_menu">Zobrazovať menu pre úlohovú položku?</i18nString> <i18nString name="item_settings">Nastavenie položky</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Súčasný filter</i18nString> - <i18nString name="view_header">Zobrazenie úloh</i18nString> <i18nString name="item_settings_general">Všeobecné</i18nString> <i18nString name="remove_filter">Vymazať filter</i18nString> </i18n> @@ -357,7 +363,6 @@ <i18nString name="use_default_headers">Eigene Kopfzeilen verwenden?</i18nString> <i18nString name="default_headers">Anzuzeigende Attributmenge auswählen</i18nString> <i18nString name="default_headers_desc">Beispiel: "meta-title,meta-user"</i18nString> - <i18nString name="filter_update_title">Filter</i18nString> <i18nString name="filter_header">Aktueller Filter</i18nString> <i18nString name="item_settings_general">Allgemein</i18nString> <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> @@ -366,7 +371,6 @@ <i18nString name="view_search_type">Suchmodus im Aufgabenansicht</i18nString> <i18nString name="show_more_menu">"Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen</i18nString> <i18nString name="item_settings">Menüeintrageinstellungen</i18nString> - <i18nString name="view_header">Aufgabenansicht</i18nString> <i18nString name="remove_filter">Filter entfernen</i18nString> </i18n> @@ -376,12 +380,24 @@ <y>208</y> <label>initialize [await sync]</label> <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>data_sync</id> <x>368</x> <y>328</y> <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>settings</id> @@ -393,35 +409,94 @@ <id>admin</id> <logic> <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> <view>true</view> - <cancel>true</cancel> - <assign>true</assign> </logic> </roleRef> <dataGroup> - <id>form_title</id> + <id>view_dataGroup</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_header</id> + <id>view_search_type</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> + <cols>3</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>3</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>advanced_content_form</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> <cols>4</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign_0</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + <transition> + <id>filter_settings</id> + <x>400</x> + <y>48</y> + <label name="todo 23">Filter</label> + <icon>filter_alt</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> <id>filter_update</id> <cols>4</cols> <layout>grid</layout> - <title name="filter_update_title">Filter filter_autocomplete_selection @@ -526,50 +601,50 @@ - - view_dataGroup - 4 - grid - - view_search_type - - editable - required - - - 0 - 0 - 1 - 3 - - outline - - - - show_more_menu - - editable - required - - - 3 - 0 - 1 - 1 - - outline - - - + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> + <transition> + <id>header_settings</id> + <x>592</x> + <y>48</y> + <label name="todo 23">Header</label> + <icon>view_column</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> <id>view_headers</id> - <cols>5</cols> + <cols>3</cols> <layout>grid</layout> <dataRef> <id>is_header_mode_changeable</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> <action trigger="set"> trans: t.this, isChangeable: f.is_header_mode_changeable, @@ -594,44 +669,51 @@ </layout> </dataRef> <dataRef> - <id>allow_header_table_mode</id> + <id>use_default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> + <action trigger="set"> + trans: t.this, + use: f.use_default_headers, + headers: f.default_headers; + + make headers,editable on trans when { use.value } + make headers,visible on trans when { !use.value } + </action> </logic> <layout> <x>1</x> <y>0</y> <rows>1</rows> <cols>1</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>headers_mode</id> + <id>default_headers</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> <x>2</x> <y>0</y> <rows>1</rows> - <cols>2</cols> + <cols>1</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>headers_default_mode</id> + <id>allow_header_table_mode</id> <logic> <behavior>editable</behavior> - <behavior>required</behavior> </logic> <layout> - <x>4</x> - <y>0</y> + <x>0</x> + <y>1</y> <rows>1</rows> <cols>1</cols> <template>material</template> @@ -639,44 +721,52 @@ </layout> </dataRef> <dataRef> - <id>use_default_headers</id> + <id>headers_mode</id> <logic> <behavior>editable</behavior> - <action trigger="set"> - trans: t.this, - use: f.use_default_headers, - headers: f.default_headers; - - make headers,editable on trans when { use.value } - make headers,visible on trans when { !use.value } - </action> + <behavior>required</behavior> </logic> <layout> - <x>0</x> + <x>1</x> <y>1</y> <rows>1</rows> <cols>1</cols> - <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> <dataRef> - <id>default_headers</id> + <id>headers_default_mode</id> <logic> <behavior>editable</behavior> + <behavior>required</behavior> </logic> <layout> - <x>1</x> + <x>2</x> <y>1</y> <rows>1</rows> - <cols>4</cols> - <offset>0</offset> + <cols>1</cols> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> </transition> <place> <id>initialized</id> @@ -715,4 +805,18 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>filter_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> + <arc> + <id>a5</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>header_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index b68407bc78a..66f770c7188 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -25,8 +25,6 @@ <roleRef> <id>default</id> <caseLogic> - <create>false</create> - <delete>false</delete> <view>true</view> </caseLogic> </roleRef> @@ -222,13 +220,22 @@ <!-- END OF ASSOCIATED VIEW --> <!-- VIEW CONFIGURATION DATA --> - <data type="i18n"> - <id>view_header</id> + <data type="taskRef"> + <id>advanced_content_form</id> <title/> - <init name="view_header">Ticket view</init> <component> - <name>divider</name> + <!-- todo 23 headers--> + <name>task-list</name> </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] } + </action> + </actions> + </event> </data> <!-- END OF VIEW CONFIGURATION DATA --> @@ -240,8 +247,6 @@ <i18nString name="filter_header">Súčasný filter</i18nString> <i18nString name="view_configuration_type">Vybrať zobrazenie</i18nString> <i18nString name="settings">Nastavenie</i18nString> - <i18nString name="associated_view">Asociované zobrazenie</i18nString> - <i18nString name="view_header">Tiketové zobrazenie</i18nString> </i18n> <i18n locale="de"> <i18nString name="filter_autocomplete_selection">Neue Filter auswählen</i18nString> @@ -250,8 +255,6 @@ <i18nString name="update_filter">Aktualisiere die Ansicht mit dem ausgewählten Filter</i18nString> <i18nString name="view_configuration_type">Wählen Sie einen Ansichtstyp</i18nString> <i18nString name="settings">Einstellungen</i18nString> - <i18nString name="associated_view">zugehörige Ansicht</i18nString> - <i18nString name="view_header">Ticketansicht</i18nString> </i18n> <transition> @@ -260,12 +263,24 @@ <y>208</y> <label>initialize [await sync]</label> <icon>hourglass_empty</icon> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>data_sync</id> <x>368</x> <y>328</y> <label>Data sync</label> + <roleRef> + <id>system</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> </transition> <transition> <id>settings</id> @@ -277,35 +292,66 @@ <id>admin</id> <logic> <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> <view>true</view> - <cancel>true</cancel> - <assign>true</assign> </logic> </roleRef> <dataGroup> - <id>form_title</id> + <id>settings_0</id> <cols>4</cols> <layout>grid</layout> <dataRef> - <id>view_header</id> + <id>advanced_content_form</id> <logic> - <behavior>visible</behavior> + <behavior>editable</behavior> </logic> <layout> <x>0</x> <y>0</y> <rows>1</rows> <cols>4</cols> + <offset>0</offset> <template>material</template> <appearance>outline</appearance> </layout> </dataRef> </dataGroup> + <event type="assign"> + <id>assign_0</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + </transition> + <transition> + <id>next_view_settings</id> + <x>400</x> + <y>48</y> + <label name="todo 23">Next view</label> + <icon>queue_play_next</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> <dataGroup> <id>associated_view</id> <cols>4</cols> <layout>grid</layout> - <title name="associated_view">Associated view view_configuration_type @@ -364,6 +410,22 @@ + + assign + + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> </transition> <place> <id>initialized</id> @@ -402,4 +464,11 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>next_view_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java new file mode 100644 index 00000000000..cd965650498 --- /dev/null +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -0,0 +1,156 @@ +package com.netgrif.application.engine.menu; + +import com.netgrif.application.engine.TestHelper; +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody; +import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; +import com.netgrif.application.engine.petrinet.domain.I18nString; +import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.workflow.domain.Case; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ActiveProfiles({"test"}) +@ExtendWith(SpringExtension.class) +public class MenuItemServiceTest { + + @Autowired + private TestHelper testHelper; + + @Autowired + private IMenuItemService menuItemService; + + @BeforeEach + public void beforeEach() { + testHelper.truncateDbs(); + } + + @Test + public void createFilterTest() { + // todo + } + + @Test + public void updateFilterTest() { + // todo + } + + @Test + public void createMenuItemTest() { + // todo + } + + @Test + public void updateMenuItemTest() { + // todo + } + + @Test + public void createOrUpdateMenuItemTest() { + // todo + } + + @Test + public void createOrIgnoreMenuItemTest() { + // todo + } + + @Test + public void findMenuItemByIdentifierTest() { + // todo + } + + @Test + public void findMenuItemByUriAndNameTest() { + // todo + } + + @Test + public void findFolderCaseTest() { + // todo + } + + @Test + public void existsMenuItemTest() { + // todo + } + + @Test + public void moveItemTest() { + // todo + } + + @Test + public void duplicateItemTest() { + // todo + } + + @Test + public void removeChildItemFromParentTest() { + // todo + } + + @Test + public void getMenuItemDataTest() throws TransitionNotExecutableException { + assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId")); + + Case menuItemCase = createDefaultMenuItem("my_menu_item", + new I18nString("This is name", Map.of("sk", "Toto je nazov"))); + + Map<String, List<Field<?>>> resultMap = menuItemService.getMenuItemData(menuItemCase.getStringId()); + assertEquals(3, resultMap.size()); + assertTrue(resultMap.containsKey("menu_item")); + assertTrue(resultMap.containsKey("tabbed_case_view")); + assertTrue(resultMap.containsKey("tabbed_task_view")); + } + + @Test + public void getAvailableViewsAsOptionsByIsPrimaryTest() { + // todo + } + + @Test + public void getAvailableViewsAsOptionsByViewIdentifierTest() { + // todo + } + + private Case createDefaultMenuItem(String identifier, I18nString name) throws TransitionNotExecutableException { + FilterBody filterBody = new FilterBody(); + filterBody.setTitle(new I18nString("My case view filter")); + filterBody.setQuery("processIdentifier:process1"); + filterBody.setType("Case"); + filterBody.setAllowedNets(List.of("process1")); + filterBody.setIcon("home"); + filterBody.setVisibility("private"); + + TabbedCaseViewBody caseView = new TabbedCaseViewBody(); + caseView.setFilterBody(filterBody); + caseView.setRequireTitleInCreation(false); + caseView.setChainedView(new TabbedTaskViewBody()); + + MenuItemBody menuItemBody = new MenuItemBody(); + menuItemBody.setUri("/"); + menuItemBody.setIdentifier(identifier); + menuItemBody.setMenuIcon("home"); + menuItemBody.setMenuName(name); + menuItemBody.setTabIcon("folder"); + menuItemBody.setTabName(name); + menuItemBody.setView(caseView); + + return menuItemService.createMenuItem(menuItemBody); + } +} From 21fd5c38bfcf7240da38bd893a7dd862d79b29e0 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Thu, 7 May 2026 11:41:20 +0200 Subject: [PATCH 066/174] [ETASK-23] Dynamic view configuration - fix resolving menu item in the menu - add todos --- .../engine/menu/domain/MenuItemBody.java | 2 + .../engine/menu/domain/MenuItemConstants.java | 2 + .../menu/domain/configurations/ViewBody.java | 2 + .../domain/configurations/ViewConstants.java | 2 + .../engine/menu/service/MenuItemService.java | 37 +--- .../service/interfaces/IMenuItemService.java | 5 +- .../engine/menu/web/MenuController.java | 14 +- .../responsebodies/MenuItemDataResponse.java | 10 +- .../engine-processes/menu/menu_item.xml | 174 +++++++++++++++++- .../menu/tabbed_case_view_configuration.xml | 149 +++++++++++++++ .../menu/tabbed_task_view_configuration.xml | 106 +++++++++++ .../menu/tabbed_ticket_view_configuration.xml | 35 ++++ .../engine/menu/MenuItemServiceTest.java | 24 ++- 13 files changed, 507 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 9637f9ddeb8..5af588b2fa3 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -181,6 +181,8 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas List.of(viewCase.getStringId())); String taskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_SETTINGS_ID); outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_FORM, FieldType.TASK_REF, List.of(taskId)); + String allDataTaskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_ALL_MENU_DATA_ID); + outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM, FieldType.TASK_REF, List.of(allDataTaskId)); } return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java index 0c1912a69fc..320c0f08ebf 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -28,8 +28,10 @@ public class MenuItemConstants { public static final String FIELD_IS_AUTO_SELECT = "is_auto_select"; public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; + public static final String FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM = "view_configuration_all_data_form"; public static final String TRANS_SETTINGS_ID = "item_settings"; public static final String TRANS_INIT_ID = "system_initialize"; public static final String TRANS_SYNC_ID = "data_sync"; + public static final String TRANS_ALL_MENU_DATA = "all_menu_data"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java index d636b6e60b1..7496fa45998 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java @@ -73,6 +73,8 @@ public ToDataSetOutcome toDataSet(Case associatedViewCase, Case filterCase) { List.of(associatedViewCase.getStringId())); String taskId = MenuItemUtils.findTaskIdInCase(associatedViewCase, ViewConstants.TRANS_SETTINGS_ID); outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_FORM, FieldType.TASK_REF, List.of(taskId)); + String allDataTaskId = MenuItemUtils.findTaskIdInCase(associatedViewCase, ViewConstants.TRANS_ALL_MENU_DATA_ID); + outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM, FieldType.TASK_REF, List.of(allDataTaskId)); } if (filterCase != null) { outcome.putDataSetEntry(ViewConstants.FIELD_VIEW_FILTER_CASE, FieldType.CASE_REF, List.of(filterCase.getStringId())); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java index 74c38803f06..c0a904aa9b5 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java @@ -7,10 +7,12 @@ public class ViewConstants { public static final String FIELD_CONFIGURATION_TYPE = "view_configuration_type"; public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; + public static final String FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM = "view_configuration_all_data_form"; public static final String FIELD_VIEW_CONTAINS_FILTER = "contains_filter"; public static final String FIELD_VIEW_FILTER_CASE = "filter_case"; public static final String TRANS_INIT_ID = "initialize"; public static final String TRANS_SETTINGS_ID = "settings"; + public static final String TRANS_ALL_MENU_DATA_ID = "all_menu_data"; public static final String TRANS_SYNC_ID = "data_sync"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 4c8f3d9ae65..708c05eb4a8 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -13,10 +13,10 @@ import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriContentType; import com.netgrif.application.engine.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.petrinet.service.interfaces.IUriService; @@ -24,7 +24,6 @@ import com.netgrif.application.engine.startup.FilterRunner; import com.netgrif.application.engine.startup.ImportHelper; import com.netgrif.application.engine.workflow.domain.Case; -import com.netgrif.application.engine.workflow.domain.DataField; import com.netgrif.application.engine.workflow.domain.Task; import com.netgrif.application.engine.workflow.service.interfaces.IDataService; import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; @@ -406,42 +405,16 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { * Retrieves menu item data along with its associated view configuration data. The method collects immediate * data from the menu item case and recursively traverses through all associated view configuration cases, * aggregating their immediate data into a single map. - * + * todo 23 doc * @param caseId identifier of the menu item case * @return map where keys are process identifiers (with "_configuration" suffix removed for view cases) and * values are lists of immediate data fields from the corresponding cases */ @Override - public Map<String, List<Field<?>>> getMenuItemData(String caseId) { + public List<DataGroup> getMenuItemData(String caseId, Locale locale) { Case menuItemCase = workflowService.findOne(caseId); - Map<String, List<Field<?>>> immediateDataMap = new HashMap<>(); - immediateDataMap.put(menuItemCase.getProcessIdentifier(), menuItemCase.getImmediateData()); - Optional<String> viewCaseIdOpt = getNextViewCaseId(menuItemCase); - if (viewCaseIdOpt.isEmpty()) { - return immediateDataMap; - } - - do { - Case viewCase = workflowService.findOne(viewCaseIdOpt.get()); - immediateDataMap.put(viewCase.getProcessIdentifier().replace("_configuration", ""), - viewCase.getImmediateData()); - viewCaseIdOpt = getNextViewCaseId(viewCase); - } while (viewCaseIdOpt.isPresent()); - - return immediateDataMap; - } - - protected Optional<String> getNextViewCaseId(Case parentCase) { - DataField viewConfigurationIdDataField = parentCase.getDataField(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID); - if (viewConfigurationIdDataField == null) { - return Optional.empty(); - } - @SuppressWarnings("unchecked") - List<String> viewConfigurationIdValue = (List<String>) viewConfigurationIdDataField.getValue(); - if (viewConfigurationIdValue == null || viewConfigurationIdValue.isEmpty()) { - return Optional.empty(); - } - return Optional.ofNullable(viewConfigurationIdValue.get(0)); + String taskId = MenuItemUtils.findTaskIdInCase(menuItemCase, MenuItemConstants.TRANS_ALL_MENU_DATA); + return dataService.getDataGroups(taskId, locale).getData(); } protected Case findCase(String processIdentifier, String query) { diff --git a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java index 929f348cf10..90cd699331b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java @@ -3,14 +3,15 @@ import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.MenuItemView; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.UriNode; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.workflow.domain.Case; import com.netgrif.application.engine.petrinet.domain.dataset.MapOptionsField; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; @@ -29,7 +30,7 @@ public interface IMenuItemService { void moveItem(Case item, String destUri) throws TransitionNotExecutableException; Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; Case removeChildItemFromParent(String folderId, Case childItem); - Map<String, List<Field<?>>> getMenuItemData(String caseId); + List<DataGroup> getMenuItemData(String caseId, Locale locale); /** * Gets all tabbed or non-tabbed views diff --git a/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java index 27ffee572e9..32e32f3fdde 100644 --- a/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java +++ b/src/main/java/com/netgrif/application/engine/menu/web/MenuController.java @@ -2,7 +2,7 @@ import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.web.responsebodies.MenuItemDataResponse; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; @@ -12,13 +12,14 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; import java.util.Base64; import java.util.List; -import java.util.Map; +import java.util.Locale; @Slf4j @RestController @@ -29,17 +30,20 @@ public class MenuController { private final IMenuItemService menuItemService; + // todo 23 menu item authorization @Operation(summary = "Get relevant data for the menu item", security = {@SecurityRequirement(name = "BasicAuth")}) @GetMapping(value = "/{encodedCaseId}", produces = MediaTypes.HAL_JSON_VALUE) - public EntityModel<MenuItemDataResponse> getMenuItemData(String encodedCaseId) { + public EntityModel<MenuItemDataResponse> getMenuItemData(@PathVariable("encodedCaseId") String encodedCaseId, Locale locale) { try { String caseId = new String(Base64.getDecoder().decode(encodedCaseId)); - Map<String, List<Field<?>>> immediateDataMap = menuItemService.getMenuItemData(caseId); - return EntityModel.of(new MenuItemDataResponse(immediateDataMap)); + List<DataGroup> dataGroups = menuItemService.getMenuItemData(caseId, locale); + return EntityModel.of(new MenuItemDataResponse(dataGroups)); } catch (Exception e) { log.error("Getting menu item data failed", e); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Getting menu item data failed", e); } } + + // todo 23 search with authorization } diff --git a/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java index 43f265a9547..78e7851e573 100644 --- a/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java +++ b/src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java @@ -1,12 +1,10 @@ package com.netgrif.application.engine.menu.web.responsebodies; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RequiredArgsConstructor public class MenuItemDataResponse { @@ -15,9 +13,5 @@ public class MenuItemDataResponse { * Map containing menu item data where key is the view type and value is a list of immediate fields. */ @Getter - private final Map<String, List<Field<?>>> data; - - public MenuItemDataResponse() { - this(new HashMap<>()); - } + private final List<DataGroup> data; } diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 1f53c563974..d9e702304d2 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -380,7 +380,11 @@ <action trigger="get"> processes: f.this; - change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } + try { + change processes options { return configurableMenuService.getNetsByAuthorAsMapOptions(loggedUser(), org.springframework.context.i18n.LocaleContextHolder.locale) } + } catch (Exception e) { + log.error("Cannot resolve processes: ", e) + } </action> <action trigger="set"> processes: f.this, @@ -509,6 +513,10 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>view_configuration_all_data_form</id> + <title/> + </data> <data type="button"> <id>order_down</id> <title/> @@ -727,6 +735,7 @@ <x>340</x> <y>220</y> <label name="initialize">Create item</label> +<!-- todo 23 tags visible--> <icon>hourglass_empty</icon> <roleRef> <id>admin</id> @@ -1601,6 +1610,7 @@ <action> view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, + view_configuration_all_data_form: f.view_configuration_all_data_form, view_configuration_id: f.view_configuration_id; if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { @@ -1610,6 +1620,7 @@ if (view_configuration_type.value == null || view_configuration_type.value == "") { change view_configuration_id value { [] } change view_configuration_form value { [] } + change view_configuration_all_data_form value { [] } return } @@ -1619,6 +1630,7 @@ configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_id value { [configurationCase.stringId] } change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] } </action> </actions> </event> @@ -1655,6 +1667,166 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>menu_item_identifier</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>nodePath</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>menu_name</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>menu_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>menu_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>hasChildren</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>is_auto_select</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>processes_available</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>roles_available</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>add_allowed_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>allowed_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>remove_allowed_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>add_banned_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>banned_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>remove_banned_roles</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_custom_view</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>custom_view_selector</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_tabbed_view</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>tab_name</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_tab_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>tab_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>tab_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_all_data_form</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <!-- PLACES--> <place> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 39740f21fce..37ce910b0e3 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -390,6 +390,10 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>view_configuration_all_data_form</id> + <title/> + </data> <data type="taskRef"> <id>advanced_content_form</id> <title/> @@ -733,6 +737,7 @@ <action> view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, + view_configuration_all_data_form: f.view_configuration_all_data_form, view_configuration_id: f.view_configuration_id; if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { @@ -742,6 +747,7 @@ if (view_configuration_type.value == null || view_configuration_type.value == "") { change view_configuration_id value { [] } change view_configuration_form value { [] } + change view_configuration_all_data_form value { [] } return } @@ -751,6 +757,7 @@ configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_id value { [configurationCase.stringId] } change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] } </action> </actions> </event> @@ -1073,6 +1080,148 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>current_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_search_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_create_case_button</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>create_case_button_title</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>create_case_button_icon</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>create_case_button_icon_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>require_title_in_creation</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>banned_nets_in_creation</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>is_header_mode_changeable</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>allow_header_table_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_default_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_case_default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_all_data_form</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index abc71d160a9..9ad685ae90a 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -768,6 +768,112 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>current_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>merge_filters</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>remove_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_search_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_more_menu</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>is_header_mode_changeable</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>allow_header_table_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>headers_default_mode</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>use_default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>default_headers</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 66f770c7188..35dc6a471d5 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -217,6 +217,10 @@ <id>view_configuration_form</id> <title/> </data> + <data type="taskRef"> + <id>view_configuration_all_data_form</id> + <title/> + </data> <!-- END OF ASSOCIATED VIEW --> <!-- VIEW CONFIGURATION DATA --> @@ -372,6 +376,7 @@ <action> view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, + view_configuration_all_data_form: f.view_configuration_all_data_form, view_configuration_id: f.view_configuration_id; if (view_configuration_id.value != null && !view_configuration_id.value.isEmpty()) { @@ -381,6 +386,7 @@ if (view_configuration_type.value == null || view_configuration_type.value == "") { change view_configuration_id value { [] } change view_configuration_form value { [] } + change view_configuration_all_data_form value { [] } return } @@ -390,6 +396,7 @@ change view_configuration_id value { [configurationCase.stringId] } configurationCase = workflowService.findOne(configurationCase.stringId) change view_configuration_form value { [configurationCase.tasks.find { it.transition == "settings" }.task] } + change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] } </action> </actions> </event> @@ -427,6 +434,34 @@ <title/> </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>view_configuration_type</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>view_configuration_all_data_form</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index cd965650498..0f711caf38c 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -7,18 +7,22 @@ import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.petrinet.domain.I18nString; -import com.netgrif.application.engine.petrinet.domain.dataset.Field; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; +import com.netgrif.application.engine.startup.SuperCreator; import com.netgrif.application.engine.workflow.domain.Case; +import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.GetDataGroupsEventOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; +import java.util.Locale; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -33,6 +37,9 @@ public class MenuItemServiceTest { @Autowired private IMenuItemService menuItemService; + + @Autowired + private SuperCreator superCreator; @BeforeEach public void beforeEach() { @@ -106,16 +113,15 @@ public void removeChildItemFromParentTest() { @Test public void getMenuItemDataTest() throws TransitionNotExecutableException { - assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId")); + // todo 23 fix test + assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId", Locale.getDefault())); Case menuItemCase = createDefaultMenuItem("my_menu_item", new I18nString("This is name", Map.of("sk", "Toto je nazov"))); - Map<String, List<Field<?>>> resultMap = menuItemService.getMenuItemData(menuItemCase.getStringId()); - assertEquals(3, resultMap.size()); - assertTrue(resultMap.containsKey("menu_item")); - assertTrue(resultMap.containsKey("tabbed_case_view")); - assertTrue(resultMap.containsKey("tabbed_task_view")); + login(); + GetDataGroupsEventOutcome result = menuItemService.getMenuItemData(menuItemCase.getStringId(), Locale.getDefault()); + assertTrue(result != null); } @Test @@ -153,4 +159,8 @@ private Case createDefaultMenuItem(String identifier, I18nString name) throws Tr return menuItemService.createMenuItem(menuItemBody); } + + private void login() { + SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(superCreator.getLoggedSuper(), null)); + } } From 341ea391c58fde41533c059a7ce5004b9cef0034 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 11 May 2026 09:17:50 +0200 Subject: [PATCH 067/174] [ETASK-23] Dynamic view configuration - fix changing menu item by ActionDelegate - fix menu_item.manageBehaviorOfTabFields - fix menu_item move_add_node set action - improve menu_item initialization when creating by forms - fix next view selection in ticket view --- .../logic/action/ActionDelegate.groovy | 10 ++++----- .../engine-processes/menu/menu_item.xml | 7 +++++- .../tabbed_single_task_view_configuration.xml | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index d4b527dcb6c..9b8b0c338bd 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -1899,31 +1899,31 @@ class ActionDelegate { title : { cl -> def value = cl() I18nString newName = (value instanceof I18nString) ? value : new I18nString(value as String) - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_MENU_NAME): ["type": "i18n", "value": newName] ]) }, menuIcon : { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_MENU_ICON): ["type": "text", "value": value] ]) }, tabIcon : { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_TAB_ICON): ["type": "text", "value": value] ]) }, useCustomView : { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_USE_CUSTOM_VIEW): ["type": "boolean", "value": value] ]) }, customViewSelector: { cl -> def value = cl() - setData(MenuItemConstants.TRANS_SETTINGS_ID, item, [ + setData(MenuItemConstants.TRANS_SYNC_ID, item, [ (MenuItemConstants.FIELD_CUSTOM_VIEW_SELECTOR): ["type": "text", "value": value] ]) }] diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index d9e702304d2..fc9bdfddbc6 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -93,7 +93,7 @@ } </function> <function scope="process" name="manageBehaviorOfTabFields"> - { boolean useTabIcon, boolean useTabbedView, String transId = "item_settings" -> + { boolean useTabIcon, boolean useTabbedView, String transId = "view_settings" -> def settingsTrans = useCase.petriNet.transitions[transId] make [useCase.getField("use_tab_icon"), useCase.getField("tab_icon"), useCase.getField("tab_name")], @@ -193,6 +193,7 @@ prefixUri = prefixUri.replace("//","/") String newUri = prefixUri + uriService.getUriSeparator() + newNodeName.value + newUri = newUri.replace("//","/") def newNode = uriService.getOrCreate(newUri, com.netgrif.application.engine.petrinet.domain.UriContentType.CASE) change selectedUri value { splitUriPath(newNode.uriPath) } @@ -737,6 +738,7 @@ <label name="initialize">Create item</label> <!-- todo 23 tags visible--> <icon>hourglass_empty</icon> + <assignPolicy>auto</assignPolicy> <roleRef> <id>admin</id> <logic> @@ -810,12 +812,15 @@ <id>finish</id> <actions phase="pre"> <action> + name: f.menu_name, + identifier: f.menu_item_identifier, dest: f.move_dest_uri; String newUri = dest.value.join("/") newUri = newUri.replace("//","/") changeMenuItem useCase uri { newUri } + change name value { new com.netgrif.application.engine.petrinet.domain.I18nString(identifier.value) } </action> </actions> <title name="create_item_finish">Create diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 22f4853e17f..2c869531c05 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -266,6 +266,28 @@ </event> </transition> + <transition> + <id>all_menu_data</id> + <x>10</x> + <y>10</y> + <label>All menu data</label> + <roleRef> + <id>system</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>all_menu_data_0</id> + <layout>flow</layout> + <dataRef> + <id>transition_id</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + </dataGroup> + </transition> <place> <id>initialized</id> <x>496</x> From 400bf4307fd2575e757cd779d434eeb8dbbf9ae3 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 11 May 2026 13:15:17 +0200 Subject: [PATCH 068/174] [ETASK-23] Dynamic view configuration - make single task view as a primary view - update init values in menu_item.xml - update tabbed_single_task_view_configuration configurations --- .../engine/menu/domain/MenuItemView.java | 2 +- .../engine-processes/menu/menu_item.xml | 14 +- .../menu/tabbed_case_view_configuration.xml | 2 + .../tabbed_single_task_view_configuration.xml | 235 ++++++++++++++++-- .../menu/tabbed_task_view_configuration.xml | 10 +- .../menu/tabbed_ticket_view_configuration.xml | 106 +------- 6 files changed, 239 insertions(+), 130 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 1bdf5a43d54..6a1ba78b206 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -23,7 +23,7 @@ public enum MenuItemView { "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", List.of("tabbed_single_task_view"), true, true), TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, false); + "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, true); private final I18nString name; private final String identifier; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index fc9bdfddbc6..5c1d8f85c3a 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -448,6 +448,7 @@ <data type="boolean" immediate="true"> <id>use_tabbed_view</id> <title name="use_tabbed_view">Do you want to use view with tabs? + true @@ -490,7 +491,7 @@ view_configuration_type Pick view type - menuItemService.getAvailableViewsAsOptions(false, true) + menuItemService.getAvailableViewsAsOptions(true, true) use_tabbed_view: f.use_tabbed_view, @@ -508,6 +509,7 @@ tabbed_case_view_configuration tabbed_task_view_configuration tabbed_ticket_view_configuration + tabbed_single_task_view_configuration @@ -1532,7 +1534,7 @@ tab_name - hidden + editable 2 @@ -1546,7 +1548,7 @@ use_tab_icon - hidden + editable 3 @@ -1571,7 +1573,7 @@ tab_icon - hidden + editable 0 @@ -1585,7 +1587,7 @@ tab_icon_preview - hidden + visible 1 @@ -1686,6 +1688,8 @@ all_menu_data_0 flow + start + true menu_item_identifier diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 37ce910b0e3..14626ff6bab 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -1094,6 +1094,8 @@ all_menu_data_0 flow + start + true filter_autocomplete_selection diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml index 2c869531c05..6a346161c40 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml @@ -53,7 +53,7 @@ } else { change filterAutocomplete options { def findAllPredicate = { filterCase -> !selectedFilterRef.value.contains(filterCase.stringId) - && filterCase.dataSet["filter_type"].value == "Case" } + && filterCase.dataSet["filter_type"].value == "Task" } return findFilters(filterAutocomplete.value != null ? filterAutocomplete.value : "") .findAll(findAllPredicate) .collectEntries({filterCase -> [filterCase.stringId, filterCase.title]}) @@ -70,7 +70,7 @@ </data> <data type="taskRef"> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <title/> </data> <data type="i18n"> @@ -97,7 +97,7 @@ <name>autocomplete_dynamic</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -106,7 +106,7 @@ updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) </action> <action trigger="get"> - trans: t.settings, + trans: t.filter_settings, filterAutocomplete: f.this, filter_case: f.filter_case, update_filter: f.update_filter, @@ -123,7 +123,7 @@ <name>raised</name> </component> <action trigger="set"> - trans: t.settings, + trans: t.filter_settings, update_filter: f.update_filter, contains_filter: f.contains_filter, filter_case: f.filter_case, @@ -147,7 +147,7 @@ <id>filter_case</id> <title/> <action trigger="set"> - filterTaskRef: f.current_filter_preview, + filterTaskRef: f.current_task_filter_preview, contains_filter: f.contains_filter, filterCaseRef: f.filter_case; @@ -167,13 +167,33 @@ <allowedNet>filter</allowedNet> </allowedNets> </data> - - <!-- VIEW CONFIGURATION DATA --> - <data type="text"> - <id>transition_id</id> - <title name="transition_id">Transition id + + advanced_content_form + + <component> + <!-- todo 23 headers--> + <name>task-list</name> + </component> + <event type="get"> + <id>get</id> + <actions phase="pre"> + <action> + advanced_content_form: f.advanced_content_form; + change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task] } + </action> + </actions> + </event> + </data> + <data type="boolean"> + <id>show_page_header</id> + <title name="todo 23">Show page header + true + + + show_page_footer + Show page footer + false - @@ -183,7 +203,6 @@ Súčasný filter Vybrať zobrazenie Nastavenie - ID prechodu Neue Filter auswählen @@ -192,7 +211,6 @@ Aktualisiere die Ansicht mit dem ausgewählten Filter Wählen Sie einen Ansichtstyp Einstellungen - Übergangs-ID @@ -226,6 +244,7 @@ 112 settings + admin @@ -243,7 +262,7 @@ 4 grid - transition_id + show_page_header editable @@ -251,6 +270,34 @@ 0 0 1 + 2 + + outline + + + + show_page_footer + + editable + + + 2 + 0 + 1 + 2 + + outline + + + + advanced_content_form + + editable + + + 0 + 1 + 1 4 outline @@ -266,6 +313,117 @@ </event> </transition> + <transition> + <id>filter_settings</id> + <x>432</x> + <y>48</y> + <label name="todo 23">Filter</label> + <icon>filter_alt</icon> + <assignPolicy>auto</assignPolicy> + <roleRef> + <id>admin</id> + <logic> + <perform>true</perform> + </logic> + </roleRef> + <roleRef> + <id>default</id> + <logic> + <view>true</view> + </logic> + </roleRef> + <dataGroup> + <id>filter_0</id> + <cols>4</cols> + <layout>grid</layout> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>editable</behavior> + </logic> + <layout> + <x>0</x> + <y>0</y> + <rows>1</rows> + <cols>3</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>3</x> + <y>0</y> + <rows>1</rows> + <cols>1</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>1</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + <dataRef> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>2</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>outline</appearance> + </layout> + </dataRef> + <dataRef> + <id>current_task_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + <layout> + <x>0</x> + <y>3</y> + <rows>1</rows> + <cols>4</cols> + <template>material</template> + <appearance>standard</appearance> + </layout> + </dataRef> + </dataGroup> + <event type="assign"> + <id>assign</id> + <title/> + </event> + <event type="cancel"> + <id>cancel</id> + <title/> + </event> + <event type="finish"> + <id>finish_0</id> + <title/> + </event> + <event type="delegate"> + <id>delegate</id> + <title/> + </event> + </transition> <transition> <id>all_menu_data</id> <x>10</x> @@ -280,8 +438,46 @@ <dataGroup> <id>all_menu_data_0</id> <layout>flow</layout> + <alignment>start</alignment> + <stretch>true</stretch> + <dataRef> + <id>show_page_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>show_page_footer</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>filter_autocomplete_selection</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>update_filter</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>selected_filter_preview</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> <dataRef> - <id>transition_id</id> + <id>filter_header</id> + <logic> + <behavior>visible</behavior> + </logic> + </dataRef> + <dataRef> + <id>current_task_filter_preview</id> <logic> <behavior>visible</behavior> </logic> @@ -325,4 +521,11 @@ <destinationId>initialized</destinationId> <multiplicity>1</multiplicity> </arc> + <arc> + <id>a4</id> + <type>read</type> + <sourceId>initialized</sourceId> + <destinationId>filter_settings</destinationId> + <multiplicity>1</multiplicity> + </arc> </document> \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml index 9ad685ae90a..e89d84d1448 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml @@ -96,7 +96,7 @@ <title/> </data> <data type="taskRef"> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <title/> </data> <data type="i18n"> @@ -178,7 +178,7 @@ removeButton: f.remove_filter, contains_filter: f.contains_filter, mergeFilters: f.merge_filters, - filterTaskRef: f.current_filter_preview, + filterTaskRef: f.current_task_filter_preview, filterCaseRef: f.filter_case; if (filterCaseRef.value == null || filterCaseRef.value == []) { @@ -559,7 +559,7 @@ </layout> </dataRef> <dataRef> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <logic> <behavior>visible</behavior> </logic> @@ -782,6 +782,8 @@ <dataGroup> <id>all_menu_data_0</id> <layout>flow</layout> + <alignment>start</alignment> + <stretch>true</stretch> <dataRef> <id>filter_autocomplete_selection</id> <logic> @@ -807,7 +809,7 @@ </logic> </dataRef> <dataRef> - <id>current_filter_preview</id> + <id>current_task_filter_preview</id> <logic> <behavior>visible</behavior> </logic> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 35dc6a471d5..32fc9e33a2b 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -87,110 +87,6 @@ } </function> - <!-- FILTER configuration data --> - <data type="taskRef"> - <id>selected_filter_preview</id> - <title/> - </data> - <data type="taskRef"> - <id>current_filter_preview</id> - <title/> - </data> - <data type="i18n"> - <id>filter_header</id> - <title/> - <init name="filter_header">Current filter</init> - <component> - <name>divider</name> - </component> - </data> - <data type="text"> - <id>new_filter_id</id> - <title/> - </data> - <data type="boolean" immediate="true"> - <id>contains_filter</id> - <title/> - <init>false</init> - </data> - <data type="enumeration_map"> - <id>filter_autocomplete_selection</id> - <title name="filter_autocomplete_selection">Select new filter - - autocomplete_dynamic - - - trans: t.settings, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) - - - trans: t.settings, - filterAutocomplete: f.this, - filter_case: f.filter_case, - update_filter: f.update_filter, - previewTaskRef: f.selected_filter_preview; - - updateFilterAutocompleteOptions(filterAutocomplete, previewTaskRef, filter_case, update_filter, trans) - - - - update_filter - - <placeholder name="update_filter">Update view with selected filter</placeholder> - <component> - <name>raised</name> - </component> - <action trigger="set"> - trans: t.settings, - update_filter: f.update_filter, - contains_filter: f.contains_filter, - filter_case: f.filter_case, - filterAutocomplete: f.filter_autocomplete_selection; - - boolean containsFilter = filterAutocomplete.value != null && filterAutocomplete.value != "" - if (containsFilter) { - def filterCase = findCase({it._id.eq(filterAutocomplete.value)}) - if (filterCase.dataSet["filter_type"].value != "Case") { - throw new IllegalArgumentException("Filter is of wrong type. Only filter of Case type allowed.") - } - } - - change contains_filter value { containsFilter } - change filter_case value { [filterAutocomplete.value] } - change filterAutocomplete value { "" } - make update_filter,visible on trans when { true } - </action> - </data> - <data type="caseRef"> - <id>filter_case</id> - <title/> - <action trigger="set"> - filterTaskRef: f.current_filter_preview, - contains_filter: f.contains_filter, - filterCaseRef: f.filter_case; - - if (filterCaseRef.value == null || filterCaseRef.value == []) { - change filterTaskRef value { [] } - change contains_filter value { false } - return - } - - def filterCase = findCase({it._id.eq(filterCaseRef.value[0])}) - change filterTaskRef value { - return [findTask({it.caseId.eq(filterCase.stringId).and(it.transitionId.eq("view_filter"))}).stringId] - } - change contains_filter value { true } - </action> - <allowedNets> - <allowedNet>filter</allowedNet> - </allowedNets> - </data> - <!-- ASSOCIATED VIEW: this section can be removed if needed --> <data type="enumeration_map" immediate="true"> <id>view_configuration_type</id> @@ -448,6 +344,8 @@ <dataGroup> <id>all_menu_data_0</id> <layout>flow</layout> + <alignment>start</alignment> + <stretch>true</stretch> <dataRef> <id>view_configuration_type</id> <logic> From f537cb1f448e0b51cd087f3c2448aee7802bc325 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Mon, 11 May 2026 14:40:45 +0200 Subject: [PATCH 069/174] [ETASK-23] Dynamic view configuration - make single task view as untabbed too --- .../engine/menu/domain/MenuItemView.java | 30 +++++++++++-------- ...kViewBody.java => SingleTaskViewBody.java} | 10 ++++--- .../SingleTaskViewConstants.java | 6 ++++ .../TabbedSingleTaskViewConstants.java | 5 ---- .../engine-processes/menu/menu_item.xml | 2 +- ...xml => single_task_view_configuration.xml} | 4 +-- .../menu/tabbed_ticket_view_configuration.xml | 2 +- .../resources/dashboard_management_test.xml | 2 +- 8 files changed, 34 insertions(+), 27 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedSingleTaskViewBody.java => SingleTaskViewBody.java} (61%) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java delete mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java rename src/main/resources/petriNets/engine-processes/menu/{tabbed_single_task_view_configuration.xml => single_task_view_configuration.xml} (99%) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 6a1ba78b206..ddffbb2dcb2 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -14,16 +14,18 @@ * */ @Getter public enum MenuItemView { - TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", - "de", "Fallansicht mit Registerkarten")), "tabbed_case_view", List.of("tabbed_task_view"), - true, true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view", Map.of("sk", "Zobrazenie úloh v taboch", - "de", "Aufgabenansicht mit Registerkarten")), "tabbed_task_view", List.of(), true, true), - TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", - "de", "Ticketansicht mit Registerkarten")), "tabbed_ticket_view", - List.of("tabbed_single_task_view"), true, true), - TABBED_SINGLE_TASK_VIEW(new I18nString("Tabbed single task view", Map.of("sk", "Zobrazenie jednej úlohy v taboch", - "de", "Einzelaufgabenansicht mit Registerkarten")), "tabbed_single_task_view", List.of(), true, true); + TABBED_CASE_VIEW(new I18nString("Tabbed case view", + Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), + "tabbed_case_view", List.of("tabbed_task_view"), true, false, true), + TABBED_TASK_VIEW(new I18nString("Tabbed task view", + Map.of("sk", "Zobrazenie úloh v taboch", "de", "Aufgabenansicht mit Registerkarten")), + "tabbed_task_view", List.of(), true, false, true), + TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", + Map.of("sk", "Tiketové zobrazenie v taboch", "de", "Ticketansicht mit Registerkarten")), + "tabbed_ticket_view", List.of("tabbed_single_task_view"), true, false, true), + SINGLE_TASK_VIEW(new I18nString("Single task view", + Map.of("sk", "Zobrazenie jednej úlohy", "de", "Einzelaufgabenansicht")), + "single_task_view", List.of(), true, true, true); private final I18nString name; private final String identifier; @@ -32,17 +34,19 @@ public enum MenuItemView { * */ private final List<String> allowedAssociatedViews; private final boolean isTabbed; + private final boolean isUntabbed; /** * if false, the view cannot be used as first configuration of the menu_item, but can be used as secondary * (associated to another view) * */ private final boolean isPrimary; - MenuItemView(I18nString name, String identifier, List<String> allowedAssociatedViews, boolean isTabbed, boolean isPrimary) { + MenuItemView(I18nString name, String identifier, List<String> allowedAssociatedViews, boolean isTabbed, boolean isUntabbed, boolean isPrimary) { this.name = name; this.identifier = identifier; this.allowedAssociatedViews = allowedAssociatedViews; this.isTabbed = isTabbed; + this.isUntabbed = isUntabbed; this.isPrimary = isPrimary; } @@ -68,7 +72,7 @@ public static MenuItemView fromIdentifier(String identifier) { * */ public static List<MenuItemView> findAllByIsTabbedAndIsPrimary(boolean isTabbed, boolean isPrimary) { return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed && view.isPrimary == isPrimary) + .filter(view -> (view.isTabbed == isTabbed || view.isUntabbed != isTabbed) && view.isPrimary == isPrimary) .collect(Collectors.toList()); } @@ -83,7 +87,7 @@ public static List<MenuItemView> findAllByIsTabbedAndIsPrimary(boolean isTabbed, public static List<MenuItemView> findAllByIsTabbedAndParentIdentifier(boolean isTabbed, String parentIdentifier) { MenuItemView parentView = fromIdentifier(parentIdentifier); return Arrays.stream(MenuItemView.values()) - .filter(view -> view.isTabbed == isTabbed + .filter(view -> (view.isTabbed == isTabbed || view.isUntabbed != isTabbed) && parentView.getAllowedAssociatedViews().contains(view.identifier)) .collect(Collectors.toList()); } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java similarity index 61% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java index fa58689c976..06633508bed 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java @@ -10,8 +10,9 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class TabbedSingleTaskViewBody extends ViewBody { - private String transitionId; +public class SingleTaskViewBody extends ViewBody { + private boolean showPageHeader = true; + private boolean showPageFooter = false; @Override public ViewBody getAssociatedViewBody() { @@ -20,12 +21,13 @@ public ViewBody getAssociatedViewBody() { @Override public MenuItemView getViewType() { - return MenuItemView.TABBED_SINGLE_TASK_VIEW; + return MenuItemView.SINGLE_TASK_VIEW; } @Override protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { - outcome.putDataSetEntry(TabbedSingleTaskViewConstants.FIELD_TRANSITION_ID, FieldType.TEXT, this.transitionId); + outcome.putDataSetEntry(SingleTaskViewConstants.FIELD_SHOW_PAGE_HEADER, FieldType.BOOLEAN, this.showPageHeader); + outcome.putDataSetEntry(SingleTaskViewConstants.FIELD_SHOW_PAGE_FOOTER, FieldType.BOOLEAN, this.showPageFooter); return outcome; } } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java new file mode 100644 index 00000000000..2782e50e0dc --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java @@ -0,0 +1,6 @@ +package com.netgrif.application.engine.menu.domain.configurations; + +public class SingleTaskViewConstants extends ViewConstants { + public static final String FIELD_SHOW_PAGE_HEADER = "show_page_header"; + public static final String FIELD_SHOW_PAGE_FOOTER = "show_page_footer"; +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java deleted file mode 100644 index c0d6949b328..00000000000 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.netgrif.application.engine.menu.domain.configurations; - -public class TabbedSingleTaskViewConstants extends ViewConstants { - public static final String FIELD_TRANSITION_ID = "transition_id"; -} diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 5c1d8f85c3a..e29a594cd6c 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -509,7 +509,7 @@ <allowedNet>tabbed_case_view_configuration</allowedNet> <allowedNet>tabbed_task_view_configuration</allowedNet> <allowedNet>tabbed_ticket_view_configuration</allowedNet> - <allowedNet>tabbed_single_task_view_configuration</allowedNet> + <allowedNet>single_task_view_configuration</allowedNet> </allowedNets> </data> <data type="taskRef"> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml similarity index 99% rename from src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml rename to src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml index 6a346161c40..c951d9b2a10 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml @@ -1,7 +1,7 @@ <document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://petriflow.com/petriflow.schema.xsd"> - <id>tabbed_single_task_view_configuration</id> + <id>single_task_view_configuration</id> <initials>TST</initials> - <title>Tabbed single task view configuration + Single task view configuration check_box_outline_blank true false diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml index 32fc9e33a2b..07b4c103e61 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml @@ -106,7 +106,7 @@ view_configuration_id <allowedNets> - <allowedNet>tabbed_single_task_view_configuration</allowedNet> + <allowedNet>single_task_view_configuration</allowedNet> </allowedNets> </data> <data type="taskRef"> diff --git a/src/test/resources/dashboard_management_test.xml b/src/test/resources/dashboard_management_test.xml index d3f1ae15906..199b8a68b98 100644 --- a/src/test/resources/dashboard_management_test.xml +++ b/src/test/resources/dashboard_management_test.xml @@ -27,7 +27,7 @@ filterBody.getTitle(), filterBody.getIcon() ) - def singleTaskViewBody = new com.netgrif.application.engine.menu.domain.configurations.TabbedSingleTaskViewBody() + def singleTaskViewBody = new com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody() def ticketViewBody = new com.netgrif.application.engine.menu.domain.configurations.TabbedTicketViewBody() ticketViewBody.setChainedView(singleTaskViewBody) menuItemBody.setAutoSelect(true) From 4126f045a9a6b123d1caf8a4e3045694bb6144c7 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Tue, 12 May 2026 09:23:52 +0200 Subject: [PATCH 070/174] [ETASK-23] Dynamic view configuration - make task view as untabbed too --- .../logic/action/ActionDelegate.groovy | 6 ++--- .../engine/menu/domain/MenuItemView.java | 10 ++++----- ...bedTaskViewBody.java => TaskViewBody.java} | 22 +++++++++---------- ...wConstants.java => TaskViewConstants.java} | 4 ++-- .../engine-processes/menu/menu_item.xml | 2 +- .../menu/tabbed_case_view_configuration.xml | 2 +- ...ration.xml => task_view_configuration.xml} | 4 ++-- .../engine/action/MenuItemApiTest.groovy | 18 +++++++-------- .../engine/menu/MenuItemServiceTest.java | 4 ++-- 9 files changed, 36 insertions(+), 36 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedTaskViewBody.java => TaskViewBody.java} (63%) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedTaskViewConstants.java => TaskViewConstants.java} (85%) rename src/main/resources/petriNets/engine-processes/menu/{tabbed_task_view_configuration.xml => task_view_configuration.xml} (99%) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index 9b8b0c338bd..d19a3ddbe83 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -27,7 +27,7 @@ import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody -import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardManagementBody @@ -2118,13 +2118,13 @@ class ActionDelegate { caseView.setDefaultHeaders(caseDefaultHeaders) caseView.setRequireTitleInCreation(true) - ViewBody taskView = new TabbedTaskViewBody() + ViewBody taskView = new TaskViewBody() taskView.setDefaultHeaders(taskDefaultHeaders) caseView.setChainedView(taskView) return caseView } else if (filterBody.getType() == "Task") { - ViewBody taskView = new TabbedTaskViewBody() + ViewBody taskView = new TaskViewBody() taskView.setFilterBody(filterBody) taskView.setDefaultHeaders(taskDefaultHeaders) return taskView diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index ddffbb2dcb2..8ecbab35c10 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -16,13 +16,13 @@ public enum MenuItemView { TABBED_CASE_VIEW(new I18nString("Tabbed case view", Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), - "tabbed_case_view", List.of("tabbed_task_view"), true, false, true), - TABBED_TASK_VIEW(new I18nString("Tabbed task view", - Map.of("sk", "Zobrazenie úloh v taboch", "de", "Aufgabenansicht mit Registerkarten")), - "tabbed_task_view", List.of(), true, false, true), + "tabbed_case_view", List.of("task_view"), true, false, true), + TASK_VIEW(new I18nString("Task view", + Map.of("sk", "Zobrazenie úloh", "de", "Aufgabenansicht")), + "task_view", List.of(), true, true, true), TABBED_TICKET_VIEW(new I18nString("Tabbed ticket view", Map.of("sk", "Tiketové zobrazenie v taboch", "de", "Ticketansicht mit Registerkarten")), - "tabbed_ticket_view", List.of("tabbed_single_task_view"), true, false, true), + "tabbed_ticket_view", List.of("single_task_view"), true, false, true), SINGLE_TASK_VIEW(new I18nString("Single task view", Map.of("sk", "Zobrazenie jednej úlohy", "de", "Einzelaufgabenansicht")), "single_task_view", List.of(), true, true, true); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java similarity index 63% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java index 0fabe6940fe..9e572485b3b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java @@ -14,7 +14,7 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class TabbedTaskViewBody extends ViewBody { +public class TaskViewBody extends ViewBody { private Case filter; private boolean mergeFilters = true; private String viewSearchType = "fulltext_advanced"; @@ -33,29 +33,29 @@ public ViewBody getAssociatedViewBody() { @Override public MenuItemView getViewType() { - return MenuItemView.TABBED_TASK_VIEW; + return MenuItemView.TASK_VIEW; } @Override protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_MERGE_FILTERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_MERGE_FILTERS, FieldType.BOOLEAN, this.mergeFilters); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(TaskViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, + outcome.putDataSetEntry(TaskViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, this.headersMode == null ? new ArrayList<>() : this.headersMode); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(TaskViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, this.headersDefaultMode); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, this.isHeaderModeChangeable); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, this.allowHeaderTableMode); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, + outcome.putDataSetEntry(TaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); - outcome.putDataSetEntry(TabbedTaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, + outcome.putDataSetEntry(TaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, this.showMoreMenu); return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java similarity index 85% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java index f895558fa77..afc745c463b 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java @@ -1,9 +1,9 @@ package com.netgrif.application.engine.menu.domain.configurations; /** - * Here are declared constants of process tabbed_task_view_configuration.xml. + * Here are declared constants of process task_view_configuration.xml. */ -public class TabbedTaskViewConstants extends ViewConstants { +public class TaskViewConstants extends ViewConstants { public static final String FIELD_MERGE_FILTERS = "merge_filters"; public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; public static final String FIELD_DEFAULT_HEADERS = "default_headers"; diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index e29a594cd6c..2699eff5789 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -507,7 +507,7 @@ <title/> <allowedNets> <allowedNet>tabbed_case_view_configuration</allowedNet> - <allowedNet>tabbed_task_view_configuration</allowedNet> + <allowedNet>task_view_configuration</allowedNet> <allowedNet>tabbed_ticket_view_configuration</allowedNet> <allowedNet>single_task_view_configuration</allowedNet> </allowedNets> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml index 14626ff6bab..f8d9736f507 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml @@ -383,7 +383,7 @@ <id>view_configuration_id</id> <title/> <allowedNets> - <allowedNet>tabbed_task_view_configuration</allowedNet> + <allowedNet>task_view_configuration</allowedNet> </allowedNets> </data> <data type="taskRef"> diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml similarity index 99% rename from src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml rename to src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml index e89d84d1448..e09b34d8674 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml @@ -1,7 +1,7 @@ <document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://petriflow.com/petriflow.schema.xsd"> - <id>tabbed_task_view_configuration</id> + <id>task_view_configuration</id> <initials>TTV</initials> - <title>Tabbed task view configuration + Task view configuration check_box_outline_blank true false diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index e3ae062e5e0..89d93af0054 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -7,7 +7,7 @@ import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchReques import com.netgrif.application.engine.menu.domain.MenuItemConstants import com.netgrif.application.engine.menu.domain.MenuItemView import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants -import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewConstants +import com.netgrif.application.engine.menu.domain.configurations.TaskViewConstants import com.netgrif.application.engine.menu.utils.MenuItemUtils import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService import com.netgrif.application.engine.petrinet.domain.I18nString @@ -104,14 +104,14 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif/test\"", PageRequest.of(0, 1))[0] Case netgrifFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif\"", PageRequest.of(0, 1))[0] @@ -170,14 +170,14 @@ class MenuItemApiTest { assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TABBED_TASK_VIEW.identifier + assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] - assert tabbedTaskView.dataSet[TabbedTaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_FILTER_CASE].value == [] + assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" } @Test diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index 0f711caf38c..dffd8056049 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -4,7 +4,7 @@ import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody; -import com.netgrif.application.engine.menu.domain.configurations.TabbedTaskViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; @@ -146,7 +146,7 @@ private Case createDefaultMenuItem(String identifier, I18nString name) throws Tr TabbedCaseViewBody caseView = new TabbedCaseViewBody(); caseView.setFilterBody(filterBody); caseView.setRequireTitleInCreation(false); - caseView.setChainedView(new TabbedTaskViewBody()); + caseView.setChainedView(new TaskViewBody()); MenuItemBody menuItemBody = new MenuItemBody(); menuItemBody.setUri("/"); From 64b92f03b8a8199148a1ade60cd3dedfb1b7823a Mon Sep 17 00:00:00 2001 From: chvostek Date: Tue, 12 May 2026 11:09:18 +0200 Subject: [PATCH 071/174] [ETASK-23] Dynamic view configuration - make case view as untabbed too --- .../logic/action/ActionDelegate.groovy | 4 +- .../engine/menu/domain/MenuItemView.java | 6 +- ...bedCaseViewBody.java => CaseViewBody.java} | 30 +++++----- ...wConstants.java => CaseViewConstants.java} | 4 +- .../engine/menu/service/MenuItemService.java | 27 +++++---- ...ration.xml => case_view_configuration.xml} | 60 +++++++++++++++---- .../engine-processes/menu/menu_item.xml | 7 ++- .../engine/action/MenuItemApiTest.groovy | 30 +++++----- .../engine/menu/MenuItemServiceTest.java | 4 +- 9 files changed, 110 insertions(+), 62 deletions(-) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedCaseViewBody.java => CaseViewBody.java} (60%) rename src/main/java/com/netgrif/application/engine/menu/domain/configurations/{TabbedCaseViewConstants.java => CaseViewConstants.java} (90%) rename src/main/resources/petriNets/engine-processes/menu/{tabbed_case_view_configuration.xml => case_view_configuration.xml} (96%) diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy index d19a3ddbe83..e1bc462a28c 100644 --- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy +++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy @@ -26,7 +26,7 @@ import com.netgrif.application.engine.mail.interfaces.IMailService import com.netgrif.application.engine.menu.domain.FilterBody import com.netgrif.application.engine.menu.domain.MenuItemBody import com.netgrif.application.engine.menu.domain.MenuItemConstants -import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody import com.netgrif.application.engine.menu.domain.configurations.ViewBody import com.netgrif.application.engine.menu.domain.dashboard.DashboardItemBody @@ -2113,7 +2113,7 @@ class ActionDelegate { protected ViewBody createLegacyMenuItemViews(FilterBody filterBody, List caseDefaultHeaders = null, List taskDefaultHeaders = null) { if (filterBody.getType() == "Case") { - ViewBody caseView = new TabbedCaseViewBody() + ViewBody caseView = new CaseViewBody() caseView.setFilterBody(filterBody) caseView.setDefaultHeaders(caseDefaultHeaders) caseView.setRequireTitleInCreation(true) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java index 8ecbab35c10..5061dc31a6f 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemView.java @@ -14,9 +14,9 @@ * */ @Getter public enum MenuItemView { - TABBED_CASE_VIEW(new I18nString("Tabbed case view", - Map.of("sk", "Zobrazenie prípadov v taboch", "de", "Fallansicht mit Registerkarten")), - "tabbed_case_view", List.of("task_view"), true, false, true), + CASE_VIEW(new I18nString("Case view", + Map.of("sk", "Zobrazenie prípadov", "de", "Fallansicht")), + "case_view", List.of("task_view"), true, true, true), TASK_VIEW(new I18nString("Task view", Map.of("sk", "Zobrazenie úloh", "de", "Aufgabenansicht")), "task_view", List.of(), true, true, true), diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java similarity index 60% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java index 661ff73eaa5..baf76eff3ba 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java @@ -14,7 +14,7 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class TabbedCaseViewBody extends ViewBody { +public class CaseViewBody extends ViewBody { private String viewSearchType = "fulltext_advanced"; private I18nString createCaseButtonTitle; private String createCaseButtonIcon = "add"; @@ -38,39 +38,39 @@ public ViewBody getAssociatedViewBody() { @Override public MenuItemView getViewType() { - return MenuItemView.TABBED_CASE_VIEW; + return MenuItemView.CASE_VIEW; } @Override protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(CaseViewConstants.FIELD_VIEW_SEARCH_TYPE, FieldType.ENUMERATION_MAP, this.viewSearchType); if (this.createCaseButtonTitle != null) { - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, + outcome.putDataSetEntry(CaseViewConstants.FIELD_CREATE_CASE_BUTTON_TITLE, FieldType.I18N, this.createCaseButtonTitle); } - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, + outcome.putDataSetEntry(CaseViewConstants.FIELD_CREATE_CASE_BUTTON_ICON, FieldType.TEXT, this.createCaseButtonIcon); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_REQUIRE_TITLE_IN_CREATION, FieldType.BOOLEAN, this.requireTitleInCreation); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_SHOW_CREATE_CASE_BUTTON, FieldType.BOOLEAN, this.showCreateCaseButton); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, + outcome.putDataSetEntry(CaseViewConstants.FIELD_BANNED_NETS_IN_CREATION, FieldType.TEXT, this.bannedNetsInCreation); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, this.showMoreMenu); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_ALLOW_HEADER_TABLE_MODE, FieldType.BOOLEAN, this.allowHeaderTableMode); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, + outcome.putDataSetEntry(CaseViewConstants.FIELD_HEADERS_MODE, FieldType.MULTICHOICE_MAP, this.headersMode == null ? new ArrayList<>() : this.headersMode); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, + outcome.putDataSetEntry(CaseViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP, this.headersDefaultMode); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, + outcome.putDataSetEntry(CaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT, this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, this.isHeaderModeChangeable); - outcome.putDataSetEntry(TabbedCaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java similarity index 90% rename from src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java rename to src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index b5c8ef2361b..b26ce065485 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -1,9 +1,9 @@ package com.netgrif.application.engine.menu.domain.configurations; /** - * Here are declared constants of process tabbed_case_view_configuration.xml. + * Here are declared constants of process case_view_configuration.xml. */ -public class TabbedCaseViewConstants extends ViewConstants { +public class CaseViewConstants extends ViewConstants { public static final String FIELD_NEW_FILTER_ID = "new_filter_id"; public static final String FIELD_DEFAULT_HEADERS = "default_headers"; public static final String FIELD_REQUIRE_TITLE_IN_CREATION = "require_title_in_creation"; diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 708c05eb4a8..27a5f80c6c7 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -124,7 +124,7 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce Case viewCase = null; if (body.hasView()) { - viewCase = createView(body.getView()); + viewCase = createView(body.getView(), body.isUseTabbedView()); } ToDataSetOutcome dataSetOutcome = body.toDataSet(parentItemCase.getStringId(), nodePath, viewCase); menuItemCase = setDataWithExecute(menuItemCase, MenuItemConstants.TRANS_INIT_ID, dataSetOutcome.getDataSet()); @@ -150,7 +150,7 @@ public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNo } Case viewCase = findView(itemCase); - viewCase = handleView(viewCase, body.getView()); + viewCase = handleView(viewCase, body.getView(), body.isUseTabbedView()); ToDataSetOutcome dataSetOutcome = body.toDataSet(viewCase); itemCase = setData(itemCase, MenuItemConstants.TRANS_SYNC_ID, dataSetOutcome.getDataSet()); log.debug("Updated menu item case [{}] with identifier [{}].", itemCase.getStringId(), body.getIdentifier()); @@ -475,30 +475,30 @@ protected Case findCaseInCaseRef(Case useCase, String caseRefId) { } } - protected Case handleView(Case existingViewCase, ViewBody body) throws TransitionNotExecutableException { + protected Case handleView(Case existingViewCase, ViewBody body, boolean isTabbed) throws TransitionNotExecutableException { if (mustUpdateView(existingViewCase, body)) { - return updateView(existingViewCase, body); + return updateView(existingViewCase, body, isTabbed); } else if (mustCreateView(existingViewCase, body)) { - return createView(body); + return createView(body, isTabbed); } else if (mustRemoveView(existingViewCase, body)) { removeView(existingViewCase); return null; } else if (mustRemoveAndCreateView(existingViewCase, body)) { removeView(existingViewCase); - return createView(body); + return createView(body, isTabbed); } else { return null; } } - protected Case createView(ViewBody body) throws TransitionNotExecutableException { + protected Case createView(ViewBody body, boolean isTabbed) throws TransitionNotExecutableException { IUser loggedUser = userService.getLoggedOrSystem(); Case viewCase = createCase(body.getViewProcessIdentifier(), body.getViewProcessIdentifier(), - loggedUser.transformToLoggedUser()); + loggedUser.transformToLoggedUser(), isTabbed); Case associatedViewCase = null; if (body.hasAssociatedView()) { - associatedViewCase = createView(body.getAssociatedViewBody()); + associatedViewCase = createView(body.getAssociatedViewBody(), isTabbed); } Case filterCase = null; if (body.getFilterBody() != null) { @@ -516,12 +516,12 @@ protected Case createView(ViewBody body) throws TransitionNotExecutableException return viewCase; } - protected Case updateView(Case viewCase, ViewBody body) throws TransitionNotExecutableException { + protected Case updateView(Case viewCase, ViewBody body, boolean isTabbed) throws TransitionNotExecutableException { Case filterCase = findFilter(viewCase); filterCase = handleFilter(filterCase, body.getFilterBody()); Case associatedViewCase = findView(viewCase); - associatedViewCase = handleView(associatedViewCase, body.getAssociatedViewBody()); + associatedViewCase = handleView(associatedViewCase, body.getAssociatedViewBody(), isTabbed); ToDataSetOutcome outcome = body.toDataSet(associatedViewCase, filterCase); viewCase = setData(viewCase, ViewConstants.TRANS_SYNC_ID, outcome.getDataSet()); @@ -699,6 +699,11 @@ protected Case createCase(String identifier, String title, LoggedUser loggedUser return workflowService.createCaseByIdentifier(identifier, title, "", loggedUser).getCase(); } + protected Case createCase(String identifier, String title, LoggedUser loggedUser, boolean isTabbed) { + return workflowService.createCaseByIdentifier(identifier, title, "", loggedUser, + Map.of("is_tabbed", String.valueOf(isTabbed))).getCase(); + } + protected Case setData(Case useCase, String transId, Map> dataSet) { String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId); return setData(taskId, dataSet); diff --git a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml similarity index 96% rename from src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml rename to src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index f8d9736f507..a088abb4cff 100644 --- a/src/main/resources/petriNets/engine-processes/menu/tabbed_case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -1,7 +1,7 @@ - tabbed_case_view_configuration - TCV - Tabbed case view configuration + case_view_configuration + CVC + Case view configuration check_box_outline_blank true false @@ -29,10 +29,19 @@ + + view_create + + + is_tabbed_var_arc: f.is_tabbed_var_arc; + change is_tabbed_var_arc value { Boolean.valueOf(params["is_tabbed"]) ? 1 : 0 } + + + view_delete - + removeViewCase() @@ -406,14 +415,22 @@ advanced_content_form: f.advanced_content_form; - change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task, - useCase.tasks.find { taskPair -> taskPair.transition == "create_case_btn_settings"}.task, - useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task, - useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] } + String nextViewTaskId = useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}?.task + def taskIds = [useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "create_case_btn_settings"}.task, + useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task] + if (nextViewTaskId != null) { + taskIds.add(nextViewTaskId) + } + change advanced_content_form value { taskIds } + + is_tabbed_var_arc + Is tabbed + @@ -696,7 +713,7 @@ next_view_settings - 560 + 336 48 queue_play_next @@ -927,7 +944,7 @@ header_settings - 336 + 560 48 view_column @@ -1240,6 +1257,14 @@ 1 false + + is_tabbed + 240 + 48 + + 0 + false + a1 read @@ -1289,4 +1314,19 @@ create_case_btn_settings 1 + + a8 + regular + initialize + is_tabbed + 1 + is_tabbed_var_arc + + + a9 + read + is_tabbed + next_view_settings + 1 + \ No newline at end of file diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 2699eff5789..140c69c6379 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -506,7 +506,7 @@ view_configuration_id <allowedNets> - <allowedNet>tabbed_case_view_configuration</allowedNet> + <allowedNet>case_view_configuration</allowedNet> <allowedNet>task_view_configuration</allowedNet> <allowedNet>tabbed_ticket_view_configuration</allowedNet> <allowedNet>single_task_view_configuration</allowedNet> @@ -1615,6 +1615,7 @@ <id>0</id> <actions phase="post"> <action> + use_tabbed_view: f.use_tabbed_view, view_configuration_type: f.view_configuration_type, view_configuration_form: f.view_configuration_form, view_configuration_all_data_form: f.view_configuration_all_data_form, @@ -1631,7 +1632,9 @@ return } - def configurationCase = createCase(view_configuration_type.value + "_configuration") + def configurationCase = createCase(view_configuration_type.value + "_configuration", null, "", + userService.loggedOrSystem, org.springframework.context.i18n.LocaleContextHolder.getLocale(), + ["is_tabbed":String.valueOf(use_tabbed_view.value)]) def initTask = assignTask("initialize", configurationCase) finishTask(initTask) configurationCase = workflowService.findOne(configurationCase.stringId) diff --git a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy index 89d93af0054..3c591ddd80d 100644 --- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy @@ -6,7 +6,7 @@ import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseSer import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest import com.netgrif.application.engine.menu.domain.MenuItemConstants import com.netgrif.application.engine.menu.domain.MenuItemView -import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewConstants +import com.netgrif.application.engine.menu.domain.configurations.CaseViewConstants import com.netgrif.application.engine.menu.domain.configurations.TaskViewConstants import com.netgrif.application.engine.menu.utils.MenuItemUtils import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService @@ -91,7 +91,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_BANNED_ROLES].options.containsKey("role_2:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.containsKey("role_1:filter_api_test") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.CASE_VIEW.identifier assert filter.dataSet["filter"].filterMetadata["filterType"] == "Case" assert filter.dataSet["filter"].allowedNets == ["filter", "menu_item"] @@ -101,12 +101,12 @@ class MenuItemApiTest { String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedCaseViewId != null Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title" + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier - String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false @@ -139,7 +139,7 @@ class MenuItemApiTest { Case item = getMenuItem(caze) String tabbedCaseViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) Case tabbedCaseViewBeforeChange = workflowService.findOne(tabbedCaseViewIdBeforeChange) - String tabbedTaskViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseViewBeforeChange, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewIdBeforeChange = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseViewBeforeChange, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) def newUri = uriService.getOrCreate("/netgrif/test_new", UriContentType.DEFAULT) caze = setData(caze, [ @@ -157,7 +157,7 @@ class MenuItemApiTest { assert item.dataSet[MenuItemConstants.FIELD_MENU_NAME].value.toString() == "CHANGED FILTER" assert item.dataSet[MenuItemConstants.FIELD_ALLOWED_ROLES].options.entrySet()[0].key.contains("role_2") assert item.dataSet[MenuItemConstants.FIELD_USE_TABBED_VIEW].value == true - assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.TABBED_CASE_VIEW.identifier + assert item.dataSet[MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE].value == MenuItemView.CASE_VIEW.identifier assert item.uriNodeId == newUri.stringId assert filter.dataSet["filter"].allowedNets == ["filter"] @@ -167,12 +167,12 @@ class MenuItemApiTest { String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(item, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedCaseViewId != null && tabbedCaseViewId.equals(tabbedCaseViewIdBeforeChange) Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" - assert tabbedCaseView.dataSet[TabbedCaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == true + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_VIEW_FILTER_CASE].value[0] == filter.stringId + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_DEFAULT_HEADERS].value == "meta-title,meta-title,meta-title" + assert tabbedCaseView.dataSet[CaseViewConstants.FIELD_CONFIGURATION_TYPE].value == MenuItemView.TASK_VIEW.identifier - String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null && tabbedTaskViewId.equals(tabbedTaskViewIdBeforeChange) Case tabbedTaskView = workflowService.findOne(tabbedTaskViewId) assert tabbedTaskView.dataSet[TaskViewConstants.FIELD_VIEW_CONTAINS_FILTER].value == false @@ -354,7 +354,7 @@ class MenuItemApiTest { String tabbedCaseViewId = MenuItemUtils.getCaseIdFromCaseRef(leafItemCase, MenuItemConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedCaseViewId != null Case tabbedCaseView = workflowService.findOne(tabbedCaseViewId) - String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, TabbedCaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) + String tabbedTaskViewId = MenuItemUtils.getCaseIdFromCaseRef(tabbedCaseView, CaseViewConstants.FIELD_VIEW_CONFIGURATION_ID) assert tabbedTaskViewId != null workflowService.deleteCase(testFolder) diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index dffd8056049..c1df0ebdf78 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -3,7 +3,7 @@ import com.netgrif.application.engine.TestHelper; import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; -import com.netgrif.application.engine.menu.domain.configurations.TabbedCaseViewBody; +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -143,7 +143,7 @@ private Case createDefaultMenuItem(String identifier, I18nString name) throws Tr filterBody.setIcon("home"); filterBody.setVisibility("private"); - TabbedCaseViewBody caseView = new TabbedCaseViewBody(); + CaseViewBody caseView = new CaseViewBody(); caseView.setFilterBody(filterBody); caseView.setRequireTitleInCreation(false); caseView.setChainedView(new TaskViewBody()); From fe4f7437f4be84ff9b5151ffa85ccaddf7e81c83 Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Tue, 12 May 2026 15:50:10 +0200 Subject: [PATCH 072/174] [ETASK-23] Dynamic view configuration - fixes --- .../menu/domain/configurations/CaseViewBody.java | 2 +- .../domain/configurations/CaseViewConstants.java | 2 +- .../menu/case_view_configuration.xml | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java index baf76eff3ba..3bb18185d18 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java @@ -70,7 +70,7 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); outcome.putDataSetEntry(CaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN, this.isHeaderModeChangeable); - outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_CASE_DEFAULT_HEADERS, FieldType.BOOLEAN, + outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); return outcome; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index b26ce065485..ecadc80da20 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -17,5 +17,5 @@ public class CaseViewConstants extends ViewConstants { public static final String FIELD_HEADERS_MODE = "headers_mode"; public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; - public static final String FIELD_USE_CASE_DEFAULT_HEADERS = "use_case_default_headers"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; } diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index a088abb4cff..273ea0822ae 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -357,8 +357,8 @@ <init>true</init> </data> <data type="boolean" immediate="true"> - <id>use_case_default_headers</id> - <title name="use_case_default_headers">Use custom default headers? + use_default_headers + Use custom default headers? true @@ -462,7 +462,6 @@ Súčasný filter Zobrazenie prípadov Všeobecné - Použiť predvolené hlavičky Vybrať zobrazenie Nastavenie @@ -495,7 +494,6 @@ "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen Menüeintrageinstellungen Fallansicht - Benutzerdefinierte Standardheader verwenden? Wählen Sie einen Ansichtstyp Einstellungen @@ -994,12 +992,12 @@ - use_case_default_headers + use_default_headers editable trans: t.this, - use: f.use_case_default_headers, + use: f.use_default_headers, headers: f.default_headers; make headers,editable on trans when { use.value } @@ -1216,7 +1214,7 @@ - use_case_default_headers + use_default_headers visible From 6361d3bb41f8eef49b809267de632b9e0b6fa69e Mon Sep 17 00:00:00 2001 From: palajsamuel Date: Wed, 13 May 2026 08:42:18 +0200 Subject: [PATCH 073/174] [NAE-2424] UserRefs negative view permissions aren't resolved - negativeViewUsers resolution fix in Case and Task - added test TaskPermissionsTest with test nets view_permission_combinations.xml and view_permission_combinations_no_default.xml --- .../engine/workflow/domain/Case.java | 9 +- .../engine/workflow/domain/Task.java | 9 +- .../engine/workflow/service/TaskService.java | 5 +- .../workflow/service/WorkflowService.java | 9 +- .../workflow/TaskPermissionsTest.groovy | 138 +++++ .../view_permission_combinations.xml | 532 ++++++++++++++++++ ...iew_permission_combinations_no_default.xml | 154 +++++ 7 files changed, 843 insertions(+), 13 deletions(-) create mode 100644 src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy create mode 100644 src/test/resources/petriNets/view_permission_combinations.xml create mode 100644 src/test/resources/petriNets/view_permission_combinations_no_default.xml diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java index 1559cbe0017..9268326550c 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/Case.java @@ -350,9 +350,16 @@ public void resolveViewUserRefs() { public void resolveViewUsers() { getViewUsers(); this.viewUsers.clear(); + this.negativeViewUsers.clear(); this.users.forEach((user, perms) -> { - if (perms.containsKey(RolePermission.VIEW.getValue()) && perms.get(RolePermission.VIEW.getValue())) { + if (!perms.containsKey(RolePermission.VIEW.getValue())) { + return; + } + boolean viewPermission = perms.get(RolePermission.VIEW.getValue()); + if(viewPermission){ viewUsers.add(user); + } else { + negativeViewUsers.add(user); } }); } diff --git a/src/main/java/com/netgrif/application/engine/workflow/domain/Task.java b/src/main/java/com/netgrif/application/engine/workflow/domain/Task.java index 75d7e999c40..795059a595a 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/domain/Task.java +++ b/src/main/java/com/netgrif/application/engine/workflow/domain/Task.java @@ -329,9 +329,16 @@ public void resolveViewUserRefs() { public void resolveViewUsers() { getViewUsers(); this.viewUsers.clear(); + this.negativeViewUsers.clear(); this.users.forEach((role, perms) -> { - if (perms.containsKey(RolePermission.VIEW.getValue()) && perms.get(RolePermission.VIEW.getValue())) { + if (!perms.containsKey(RolePermission.VIEW.getValue())) { + return; + } + boolean viewPermission = perms.get(RolePermission.VIEW.getValue()); + if (viewPermission) { viewUsers.add(role); + } else { + negativeViewUsers.add(role); } }); } diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java b/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java index 2a045ffad93..7530e68d60e 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java +++ b/src/main/java/com/netgrif/application/engine/workflow/service/TaskService.java @@ -784,12 +784,9 @@ public void resolveUserRef(Case useCase) { @Override public Task resolveUserRef(Task task, Case useCase) { task.getUsers().clear(); - task.getNegativeViewUsers().clear(); task.getUserRefs().forEach((id, permission) -> { List userIds = getExistingUsers((UserListFieldValue) useCase.getDataSet().get(id).getValue()); - if (userIds != null && userIds.size() != 0 && permission.containsKey("view") && !permission.get("view")) { - task.getNegativeViewUsers().addAll(userIds); - } else if (userIds != null && userIds.size() != 0) { + if (userIds != null && !userIds.isEmpty()) { task.addUsers(new HashSet<>(userIds), permission); } }); diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index 472df01d5ae..d152707cfac 100644 --- a/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -215,7 +215,6 @@ public long count(Map request, LoggedUser user, Locale locale) { @Override public Case resolveUserRef(Case useCase) { useCase.getUsers().clear(); - useCase.getNegativeViewUsers().clear(); useCase.getUserRefs().forEach((id, permission) -> { resolveUserRefPermissions(useCase, id, permission); }); @@ -226,12 +225,8 @@ public Case resolveUserRef(Case useCase) { private void resolveUserRefPermissions(Case useCase, String userListId, Map permission) { List userIds = getExistingUsers((UserListFieldValue) useCase.getDataSet().get(userListId).getValue()); - if (userIds != null && userIds.size() != 0) { - if (permission.containsKey("view") && !permission.get("view")) { - useCase.getNegativeViewUsers().addAll(userIds); - } else { - useCase.addUsers(new HashSet<>(userIds), permission); - } + if (userIds != null && !userIds.isEmpty()) { + useCase.addUsers(new HashSet<>(userIds), permission); } } diff --git a/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy b/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy new file mode 100644 index 00000000000..5cbd7bd590c --- /dev/null +++ b/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy @@ -0,0 +1,138 @@ +package com.netgrif.application.engine.workflow + +import com.netgrif.application.engine.TestHelper +import com.netgrif.application.engine.auth.domain.IUser +import com.netgrif.application.engine.auth.domain.User +import com.netgrif.application.engine.auth.domain.UserState +import com.netgrif.application.engine.auth.service.interfaces.IUserService +import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService +import com.netgrif.application.engine.elastic.web.requestbodies.ElasticTaskSearchRequest +import com.netgrif.application.engine.petrinet.domain.PetriNet +import com.netgrif.application.engine.petrinet.domain.VersionType +import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.ActionDelegate +import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService +import com.netgrif.application.engine.workflow.domain.Case +import com.netgrif.application.engine.workflow.domain.Task +import com.netgrif.application.engine.workflow.service.interfaces.IDataService +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService +import com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.TaskSearchCaseRequest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.context.i18n.LocaleContextHolder +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.junit.jupiter.SpringExtension +import groovy.json.JsonBuilder + +import java.util.stream.Collectors + +@SpringBootTest +@ActiveProfiles(["test"]) +@ExtendWith(SpringExtension.class) +class TaskPermissionsTest { + + @Autowired + private IElasticTaskService elasticTaskService + + @Autowired + private IWorkflowService workflowService + + @Autowired + private IPetriNetService petriNetService + + @Autowired + private IUserService userService + + @Autowired + private IDataService dataService + + @Autowired + private ActionDelegate actionDelegate + + @Autowired + private TestHelper testHelper + + private static final String TEST_NET = "view_permission_combinations.xml" + private static final String TEST_NET_NO_DEFAULT = "view_permission_combinations_no_default.xml" + private static Case testCase + private static Case testCaseNoDefault + private static Map testUsers = [:] + + @BeforeEach() + void init() { + testHelper.truncateDbs() + actionDelegate.outcomes = [] + + PetriNet net = petriNetService.importPetriNet(new FileInputStream("src/test/resources/petriNets/" + TEST_NET), VersionType.MAJOR, userService.getLoggedOrSystem().transformToLoggedUser()).getNet() + PetriNet netNoDefault = petriNetService.importPetriNet(new FileInputStream("src/test/resources/petriNets/" + TEST_NET_NO_DEFAULT), VersionType.MAJOR, userService.getLoggedOrSystem().transformToLoggedUser()).getNet() + + testCase = workflowService.createCaseByIdentifier(net.identifier, "Test case", "", userService.getLoggedOrSystem().transformToLoggedUser()).getCase() + testCaseNoDefault = workflowService.createCaseByIdentifier(netNoDefault.identifier, "Test case with no default", "", userService.getLoggedOrSystem().transformToLoggedUser()).getCase() + + [ + new User("no_permissions@mail.com", "password", "No", "Permissions"), + new User("has_role@mail.com", "password", "Has", "Role"), + new User("in_userRef@mail.com", "password", "In", "UserRef"), + new User("both_permissions@mail.com", "password", "Both", "Permissions") + ].each { + it.setState(UserState.ACTIVE) + testUsers.put(it.getEmail(), userService.saveNew(it)) + } + + List withRoles = [testUsers.get("has_role@mail.com"), testUsers.get("both_permissions@mail.com")] + List inUserRef = [testUsers.get("in_userRef@mail.com").stringId, testUsers.get("both_permissions@mail.com").stringId] + + withRoles.forEach { + testUsers.put(it.getEmail(), userService.addRole(testUsers.get(it.getEmail()), net.roles.values().find { role -> role.importId == "process_role" }.stringId)) + testUsers.put(it.getEmail(), userService.addRole(testUsers.get(it.getEmail()), netNoDefault.roles.values().find { role -> role.importId == "process_role_no_default" }.stringId)) + } + + testCase = actionDelegate.setData("t_001", testCase, [ + "users": [ + "type": "userList", + "value": inUserRef + ] + ]).getCase() + + testCaseNoDefault = actionDelegate.setData("t_007", testCaseNoDefault, [ + "users": [ + "type": "userList", + "value": inUserRef + ] + ]).getCase() + def a = [] + } + + @Test + void testViewPermissions() { + def map = [:] + ElasticTaskSearchRequest request = new ElasticTaskSearchRequest() + request.useCase = [new TaskSearchCaseRequest(testCase.stringId, testCase.title)] + + ElasticTaskSearchRequest request2 = new ElasticTaskSearchRequest() + request2.useCase = [new TaskSearchCaseRequest(testCaseNoDefault.stringId, testCaseNoDefault.title)] + + testUsers.forEach( (key, value) -> { + Page tasks = elasticTaskService.search([request], + value.transformToLoggedUser(), + Pageable.unpaged(), LocaleContextHolder.getLocale(), false) + List list = new ArrayList<>(tasks.content).stream().map(task -> task.transitionId).collect(Collectors.toList()).sort() + + + Page tasks2 = elasticTaskService.search([request2], + value.transformToLoggedUser(), + Pageable.unpaged(), LocaleContextHolder.getLocale(), false) + List list2 = new ArrayList<>(tasks2.content).stream().map(task -> task.transitionId).collect(Collectors.toList()).sort() + + map.put(key, [ + "Default enabled": "$key -> number of found tasks with default role ENABLED: " + list.size() + "; tasks found: $list", + "Default DISabled": "$key -> number of found tasks with default role DISABLED: " + list2.size() + "; tasks found: $list2" + ]) + }) + println(new JsonBuilder(map).toPrettyString()) + } +} diff --git a/src/test/resources/petriNets/view_permission_combinations.xml b/src/test/resources/petriNets/view_permission_combinations.xml new file mode 100644 index 00000000000..8cf5d10d3d7 --- /dev/null +++ b/src/test/resources/petriNets/view_permission_combinations.xml @@ -0,0 +1,532 @@ + + + view_permission_combinations + 1.0.0 + VPC + View Permission Combinations + true + + + process_role + Process Role + + + + users + Users + + + + t_001 + 100 + 100 + + + process_role + + true + + + + default + + true + + + + users + + true + + + + + + t_002 + 300 + 100 + + + process_role + + true + + + + default + + true + + + + users + + false + + + + + + t_003 + 500 + 100 + + + process_role + + true + + + + default + + true + + + + + + t_004 + 700 + 100 + + + process_role + + true + + + + default + + false + + + + users + + true + + + + + + t_005 + 900 + 100 + + + process_role + + true + + + + default + + false + + + + users + + false + + + + + + t_006 + 1100 + 100 + + + process_role + + true + + + + default + + false + + + + + + t_007 + 1300 + 100 + + + process_role + + true + + + + users + + true + + + + + + t_008 + 1500 + 100 + + + process_role + + true + + + + users + + false + + + + + + t_009 + 1700 + 100 + + + process_role + + true + + + + + + t_010 + 100 + 300 + + + process_role + + false + + + + default + + true + + + + users + + true + + + + + + t_011 + 300 + 300 + + + process_role + + false + + + + default + + true + + + + users + + false + + + + + + t_012 + 500 + 300 + + + process_role + + false + + + + default + + true + + + + + + t_013 + 700 + 300 + + + process_role + + false + + + + default + + false + + + + users + + true + + + + + + t_014 + 900 + 300 + + + process_role + + false + + + + default + + false + + + + users + + false + + + + + + t_015 + 1100 + 300 + + + process_role + + false + + + + default + + false + + + + + + t_016 + 1300 + 300 + + + process_role + + false + + + + users + + true + + + + + + t_017 + 1500 + 300 + + + process_role + + false + + + + users + + false + + + + + + t_018 + 1700 + 300 + + + process_role + + false + + + + + + t_019 + 100 + 500 + + + default + + true + + + + users + + true + + + + + + t_020 + 300 + 500 + + + default + + true + + + + users + + false + + + + + + t_021 + 500 + 500 + + + default + + true + + + + + + t_022 + 700 + 500 + + + default + + false + + + + users + + true + + + + + + t_023 + 900 + 500 + + + default + + false + + + + users + + false + + + + + + t_024 + 1100 + 500 + + + default + + false + + + + + + t_025 + 1300 + 500 + + + users + + true + + + + + + t_026 + 1500 + 500 + + + users + + false + + + + + + t_027 + 1700 + 500 + + + \ No newline at end of file diff --git a/src/test/resources/petriNets/view_permission_combinations_no_default.xml b/src/test/resources/petriNets/view_permission_combinations_no_default.xml new file mode 100644 index 00000000000..0da52e1f849 --- /dev/null +++ b/src/test/resources/petriNets/view_permission_combinations_no_default.xml @@ -0,0 +1,154 @@ + + + view_permission_combinations_no_default + 1.0.0 + VPC + View Permission Combinations With Default Role Disabled + false + + + process_role + Process Role + + + + users + Users + + + + t_007 + 1300 + 100 + + + process_role + + true + + + + users + + true + + + + + + t_008 + 1500 + 100 + + + process_role + + true + + + + users + + false + + + + + + t_009 + 1700 + 100 + + + process_role + + true + + + + + + t_016 + 1300 + 300 + + + process_role + + false + + + + users + + true + + + + + + t_017 + 1500 + 300 + + + process_role + + false + + + + users + + false + + + + + + t_018 + 1700 + 300 + + + process_role + + false + + + + + + t_025 + 1300 + 500 + + + users + + true + + + + + + t_026 + 1500 + 500 + + + users + + false + + + + + + t_027 + 1700 + 500 + + + \ No newline at end of file From dd2ec2ed86a3defef9e10459f408bbd186aa550f Mon Sep 17 00:00:00 2001 From: chvostek Date: Wed, 13 May 2026 09:44:44 +0200 Subject: [PATCH 074/174] [ETASK-23] Dynamic view configuration - fix field ids --- .../configurations/CaseViewConstants.java | 16 +-- .../configurations/TaskViewConstants.java | 16 +-- .../menu/case_view_configuration.xml | 114 +++++++++--------- .../menu/task_view_configuration.xml | 114 +++++++++--------- 4 files changed, 130 insertions(+), 130 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index ecadc80da20..99ec5c2f2fa 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -5,17 +5,17 @@ */ public class CaseViewConstants extends ViewConstants { public static final String FIELD_NEW_FILTER_ID = "new_filter_id"; - public static final String FIELD_DEFAULT_HEADERS = "default_headers"; + public static final String FIELD_DEFAULT_HEADERS = "case_default_headers"; public static final String FIELD_REQUIRE_TITLE_IN_CREATION = "require_title_in_creation"; - public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; + public static final String FIELD_VIEW_SEARCH_TYPE = "case_view_search_type"; public static final String FIELD_CREATE_CASE_BUTTON_TITLE = "create_case_button_title"; public static final String FIELD_CREATE_CASE_BUTTON_ICON = "create_case_button_icon"; public static final String FIELD_BANNED_NETS_IN_CREATION = "banned_nets_in_creation"; public static final String FIELD_SHOW_CREATE_CASE_BUTTON = "show_create_case_button"; - public static final String FIELD_SHOW_MORE_MENU = "show_more_menu"; - public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "allow_header_table_mode"; - public static final String FIELD_HEADERS_MODE = "headers_mode"; - public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; - public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; - public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; + public static final String FIELD_SHOW_MORE_MENU = "case_show_more_menu"; + public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "case_allow_header_table_mode"; + public static final String FIELD_HEADERS_MODE = "case_headers_mode"; + public static final String FIELD_HEADERS_DEFAULT_MODE = "case_headers_default_mode"; + public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "case_is_header_mode_changeable"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_case_default_headers"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java index afc745c463b..32e9b716f67 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java @@ -5,12 +5,12 @@ */ public class TaskViewConstants extends ViewConstants { public static final String FIELD_MERGE_FILTERS = "merge_filters"; - public static final String FIELD_VIEW_SEARCH_TYPE = "view_search_type"; - public static final String FIELD_DEFAULT_HEADERS = "default_headers"; - public static final String FIELD_HEADERS_MODE = "headers_mode"; - public static final String FIELD_HEADERS_DEFAULT_MODE = "headers_default_mode"; - public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "is_header_mode_changeable"; - public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "allow_header_table_mode"; - public static final String FIELD_USE_DEFAULT_HEADERS = "use_default_headers"; - public static final String FIELD_SHOW_MORE_MENU = "show_more_menu"; + public static final String FIELD_VIEW_SEARCH_TYPE = "task_view_search_type"; + public static final String FIELD_DEFAULT_HEADERS = "task_default_headers"; + public static final String FIELD_HEADERS_MODE = "task_headers_mode"; + public static final String FIELD_HEADERS_DEFAULT_MODE = "task_headers_default_mode"; + public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "task_is_header_mode_changeable"; + public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "task_allow_header_table_mode"; + public static final String FIELD_USE_DEFAULT_HEADERS = "use_task_default_headers"; + public static final String FIELD_SHOW_MORE_MENU = "task_show_more_menu"; } diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index 273ea0822ae..a017c3fce7e 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -227,8 +227,8 @@ - view_search_type - Search type for case view + case_view_search_type + Search type for case view @@ -299,18 +299,18 @@ - show_more_menu - Show more menu for case item? + case_show_more_menu + Show more menu for case item? false - allow_header_table_mode - Allow table mode for headers? + case_allow_header_table_mode + Allow table mode for headers? true - headers_mode - Header mode + case_headers_mode + Header mode @@ -318,23 +318,23 @@ sort,edit,search - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.case_headers_mode, + defaultMode: f.case_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.case_headers_mode, + defaultMode: f.case_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headers_default_mode - Default header mode + case_headers_default_mode + Default header mode @@ -352,18 +352,18 @@ - is_header_mode_changeable - Can header mode be changed? + case_is_header_mode_changeable + Can header mode be changed? true - use_default_headers - Use custom default headers? + use_case_default_headers + Use custom default headers? true - default_headers - Set default headers + case_default_headers + Set default headers Example: "meta-title,meta-visualId" defaultHeaders: f.this; @@ -437,11 +437,11 @@ Názov tlačidla "Nová inštancia" Identifikátor ikony tlačidla "Nová inštancia" Náhľad ikony - Predvolené hlavičky + Predvolené hlavičky Napríklad: "meta-title,meta-visualId" Zvoľte nový filter Aktualizovať zobrazenie s vybraným filtrom - Typ vyhľadávania prípadov + Typ vyhľadávania prípadov Skryté Fulltext Fulltext a rozšírené @@ -449,15 +449,15 @@ Zakázané siete pri vytváraní Uveďte identifikátory procesov oddelené čiarkou. Napríklad: mynet1,mynet2 Zobraziť tlačidlo na vytvorenie prípadu? - Zobrazovať menu pre prípadovú položku? + Zobrazovať menu pre prípadovú položku? Zoraďovanie Vyhľadávanie Upravovanie - Mód hlavičiek - Predvolený mód hlavičiek - Môže byť mód hlavičiek zmenený? - Povoliť tabuľkový mód pre hlavičky? - Použiť vlastné predvolené hlavičky? + Mód hlavičiek + Predvolený mód hlavičiek + Môže byť mód hlavičiek zmenený? + Povoliť tabuľkový mód pre hlavičky? + Použiť vlastné predvolené hlavičky? Nastavenie položky Súčasný filter Zobrazenie prípadov @@ -469,7 +469,7 @@ Schaltflächentitel "Neuer Fall" Ikone ID Ikonevorschau - Anzuzeigende Attributmenge auswählen + Anzuzeigende Attributmenge auswählen Neue Filter auswählen Beispiel: "meta-title,meta-visualId" Versteckt @@ -477,21 +477,21 @@ Sortieren Suchen Bearbeiten - Kopfzeilenmodus - Standardkopfzeilenmodus - Erlaube Änderung des Kopfzeilenmodus? - Erlaube Tabellenmodus? - Eigene Kopfzeilen verwenden? + Kopfzeilenmodus + Standardkopfzeilenmodus + Erlaube Änderung des Kopfzeilenmodus? + Erlaube Tabellenmodus? + Eigene Kopfzeilen verwenden? Aktueller Filter Allgemein Aktualisiere die Ansicht mit dem ausgewählten Filter - Suchmodus im Fallansicht + Suchmodus im Fallansicht Einfacher und erweiterter Suchmodus Erforde den Titel beim erzeugen von Fällen? Ausgeschlossene Prozesse Trenne die Prozessidentifikatoren mit einer Komma. z.B.: netz1,netz2 Schaltfläche „Fall erstellen“ anzeigen? - "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen + "Erweiterte Optionen" Taste bei einzelnen Fällen anzeigen Menüeintrageinstellungen Fallansicht Wählen Sie einen Ansichtstyp @@ -546,7 +546,7 @@ 4 grid - view_search_type + case_view_search_type editable required @@ -561,7 +561,7 @@ - show_more_menu + case_show_more_menu editable @@ -964,15 +964,15 @@ 3 grid - is_header_mode_changeable + case_is_header_mode_changeable editable required trans: t.this, - isChangeable: f.is_header_mode_changeable, - mode: f.headers_mode, - defaultMode: f.headers_default_mode; + isChangeable: f.case_is_header_mode_changeable, + mode: f.case_headers_mode, + defaultMode: f.case_headers_default_mode; make [mode, defaultMode], editable on trans when { isChangeable.value } make [mode, defaultMode], required on trans when { isChangeable.value } @@ -992,13 +992,13 @@ - use_default_headers + use_case_default_headers editable trans: t.this, - use: f.use_default_headers, - headers: f.default_headers; + use: f.use_case_default_headers, + headers: f.case_default_headers; make headers,editable on trans when { use.value } make headers,visible on trans when { !use.value } @@ -1015,7 +1015,7 @@ - default_headers + case_default_headers editable @@ -1030,7 +1030,7 @@ - allow_header_table_mode + case_allow_header_table_mode editable required @@ -1046,7 +1046,7 @@ - headers_mode + case_headers_mode editable required @@ -1062,7 +1062,7 @@ - headers_default_mode + case_headers_default_mode editable required @@ -1142,7 +1142,7 @@ - view_search_type + case_view_search_type visible @@ -1154,7 +1154,7 @@ - show_more_menu + case_show_more_menu visible @@ -1190,37 +1190,37 @@ - is_header_mode_changeable + case_is_header_mode_changeable visible - allow_header_table_mode + case_allow_header_table_mode visible - headers_mode + case_headers_mode visible - headers_default_mode + case_headers_default_mode visible - use_default_headers + use_case_default_headers visible - default_headers + case_default_headers visible diff --git a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml index e09b34d8674..c9f25059274 100644 --- a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml @@ -223,8 +223,8 @@ - view_search_type - Search type for task view + task_view_search_type + Search type for task view @@ -233,31 +233,31 @@ fulltext_advanced - headers_mode - Header mode + task_headers_mode + Header mode sort,edit - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.task_headers_mode, + defaultMode: f.task_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headersMode: f.headers_mode, - defaultMode: f.headers_default_mode, + headersMode: f.task_headers_mode, + defaultMode: f.task_headers_default_mode, holder: f.headers_options_holder; updateOptionsBasedOnValue(defaultMode, headersMode, holder) - headers_default_mode - Default header mode + task_headers_default_mode + Default header mode @@ -273,23 +273,23 @@ - is_header_mode_changeable - Can header mode be changed? + task_is_header_mode_changeable + Can header mode be changed? true - allow_header_table_mode - Allow table mode for headers? + task_allow_header_table_mode + Allow table mode for headers? true - use_default_headers - Use custom default headers? + use_task_default_headers + Use custom default headers? true - default_headers - Set default headers + task_default_headers + Set default headers Example: "meta-title,meta-user" defaultHeaders: f.this; @@ -301,8 +301,8 @@ - show_more_menu - Show more menu for task item? + task_show_more_menu + Show more menu for task item? true @@ -335,15 +335,15 @@ Vyhľadávanie Upravovanie Zjednotiť filter so základným filtrom? - Typ vyhľadávania úloh - Mód hlavičiek - Predvolený mód hlavičiek - Môže byť mód hlavičiek zmenený? - Povoliť tabuľkový mód pre hlavičky? - Použiť vlastné predvolené hlavičky? - Predvolené hlavičky + Typ vyhľadávania úloh + Mód hlavičiek + Predvolený mód hlavičiek + Môže byť mód hlavičiek zmenený? + Povoliť tabuľkový mód pre hlavičky? + Použiť vlastné predvolené hlavičky? + Predvolené hlavičky Napríklad: "meta-title,meta-user" - Zobrazovať menu pre úlohovú položku? + Zobrazovať menu pre úlohovú položku? Nastavenie položky Súčasný filter Všeobecné @@ -356,20 +356,20 @@ Sortieren Suchen Bearbeiten - Kopfzeilenmodus - Standardkopfzeilenmodus - Erlaube Änderung des Kopfzeilenmodus? - Erlaube Tabellenmodus? - Eigene Kopfzeilen verwenden? - Anzuzeigende Attributmenge auswählen + Kopfzeilenmodus + Standardkopfzeilenmodus + Erlaube Änderung des Kopfzeilenmodus? + Erlaube Tabellenmodus? + Eigene Kopfzeilen verwenden? + Anzuzeigende Attributmenge auswählen Beispiel: "meta-title,meta-user" Aktueller Filter Allgemein Aktualisiere die Ansicht mit dem ausgewählten Filter Einfacher und erweiterter Suchmodus Mit dem Basisfilter kombinieren? - Suchmodus im Aufgabenansicht - "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen + Suchmodus im Aufgabenansicht + "Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigen Menüeintrageinstellungen Filter entfernen @@ -422,7 +422,7 @@ 4 grid - view_search_type + task_view_search_type editable required @@ -437,7 +437,7 @@ - show_more_menu + task_show_more_menu editable @@ -642,14 +642,14 @@ 3 grid - is_header_mode_changeable + task_is_header_mode_changeable editable trans: t.this, - isChangeable: f.is_header_mode_changeable, - mode: f.headers_mode, - defaultMode: f.headers_default_mode; + isChangeable: f.task_is_header_mode_changeable, + mode: f.task_headers_mode, + defaultMode: f.task_headers_default_mode; make [mode, defaultMode], editable on trans when { isChangeable.value } make [mode, defaultMode], required on trans when { isChangeable.value } @@ -669,13 +669,13 @@ - use_default_headers + use_task_default_headers editable trans: t.this, - use: f.use_default_headers, - headers: f.default_headers; + use: f.use_task_default_headers, + headers: f.task_default_headers; make headers,editable on trans when { use.value } make headers,visible on trans when { !use.value } @@ -692,7 +692,7 @@ - default_headers + task_default_headers editable @@ -707,7 +707,7 @@ - allow_header_table_mode + task_allow_header_table_mode editable @@ -721,7 +721,7 @@ - headers_mode + task_headers_mode editable required @@ -736,7 +736,7 @@ - headers_default_mode + task_headers_default_mode editable required @@ -827,49 +827,49 @@ - view_search_type + task_view_search_type visible - show_more_menu + task_show_more_menu visible - is_header_mode_changeable + task_is_header_mode_changeable visible - allow_header_table_mode + task_allow_header_table_mode visible - headers_mode + task_headers_mode visible - headers_default_mode + task_headers_default_mode visible - use_default_headers + use_task_default_headers visible - default_headers + task_default_headers visible From f0940111143debfffb08b2f64f7412746ef98fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ma=C5=BE=C3=A1ri?= Date: Wed, 13 May 2026 09:47:59 +0200 Subject: [PATCH 075/174] [NAE-2424] - fix typo in role id --- .../application/engine/workflow/TaskPermissionsTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy b/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy index 5cbd7bd590c..224ee54267e 100644 --- a/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy +++ b/src/test/groovy/com/netgrif/application/engine/workflow/TaskPermissionsTest.groovy @@ -88,7 +88,7 @@ class TaskPermissionsTest { withRoles.forEach { testUsers.put(it.getEmail(), userService.addRole(testUsers.get(it.getEmail()), net.roles.values().find { role -> role.importId == "process_role" }.stringId)) - testUsers.put(it.getEmail(), userService.addRole(testUsers.get(it.getEmail()), netNoDefault.roles.values().find { role -> role.importId == "process_role_no_default" }.stringId)) + testUsers.put(it.getEmail(), userService.addRole(testUsers.get(it.getEmail()), netNoDefault.roles.values().find { role -> role.importId == "process_role" }.stringId)) } testCase = actionDelegate.setData("t_001", testCase, [ From 08ae9b02f4aa00ff30c195b09b77adbdf8e260d5 Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 14 May 2026 08:36:29 +0200 Subject: [PATCH 076/174] [ETASK-23] Dynamic view configuration - introduce empty content configuration - fix null node --- .../engine/menu/domain/MenuItemBody.java | 4 +- .../domain/configurations/CaseViewBody.java | 8 ++ .../configurations/CaseViewConstants.java | 2 + .../domain/configurations/TaskViewBody.java | 9 ++ .../configurations/TaskViewConstants.java | 2 + .../menu/case_view_configuration.xml | 107 +++++++++++++++++- .../engine-processes/menu/menu_item.xml | 47 ++++---- .../menu/task_view_configuration.xml | 105 ++++++++++++++++- 8 files changed, 250 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 5af588b2fa3..15f73c3e8c0 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -160,7 +160,9 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas outcome.putDataSetEntry(MenuItemConstants.FIELD_MENU_NAME, FieldType.I18N, this.menuName); outcome.putDataSetEntry(MenuItemConstants.FIELD_MENU_ICON, FieldType.TEXT, this.menuIcon); outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TABBED_VIEW, FieldType.BOOLEAN, this.useTabbedView); - outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_NAME, FieldType.I18N, this.tabName); + if (this.tabName != null) { + outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_NAME, FieldType.I18N, this.tabName); + } outcome.putDataSetEntry(MenuItemConstants.FIELD_TAB_ICON, FieldType.TEXT, this.tabIcon); if (this.identifier != null) { outcome.putDataSetEntry(MenuItemConstants.FIELD_IDENTIFIER, FieldType.TEXT, this.getIdentifier()); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java index 3bb18185d18..7be6230497a 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java @@ -28,6 +28,8 @@ public class CaseViewBody extends ViewBody { private List defaultHeaders; private boolean isHeaderModeChangeable = true; private boolean useDefaultHeaders = true; + private I18nString emptyContentText; + private String emptyContentIcon; private ViewBody chainedView; @@ -72,6 +74,12 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.isHeaderModeChangeable); outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN, this.useDefaultHeaders); + if (this.emptyContentText != null) { + outcome.putDataSetEntry(CaseViewConstants.FIELD_EMPTY_CONTENT_TEXT, FieldType.I18N, + this.emptyContentText); + } + outcome.putDataSetEntry(CaseViewConstants.FIELD_EMPTY_CONTENT_ICON, FieldType.TEXT, + this.emptyContentIcon); return outcome; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java index 99ec5c2f2fa..721cce1b851 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java @@ -18,4 +18,6 @@ public class CaseViewConstants extends ViewConstants { public static final String FIELD_HEADERS_DEFAULT_MODE = "case_headers_default_mode"; public static final String FIELD_IS_HEADER_MODE_CHANGEABLE = "case_is_header_mode_changeable"; public static final String FIELD_USE_DEFAULT_HEADERS = "use_case_default_headers"; + public static final String FIELD_EMPTY_CONTENT_TEXT = "case_empty_content_text"; + public static final String FIELD_EMPTY_CONTENT_ICON = "case_empty_content_icon"; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java index 9e572485b3b..7ad90df3469 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.menu.domain.MenuItemView; import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; import com.netgrif.application.engine.workflow.domain.Case; import lombok.Data; @@ -25,6 +26,8 @@ public class TaskViewBody extends ViewBody { private boolean useDefaultHeaders = true; private List defaultHeaders; private boolean showMoreMenu = true; + private I18nString emptyContentText; + private String emptyContentIcon; @Override public ViewBody getAssociatedViewBody() { @@ -57,6 +60,12 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) { this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null); outcome.putDataSetEntry(TaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN, this.showMoreMenu); + if (this.emptyContentText != null) { + outcome.putDataSetEntry(TaskViewConstants.FIELD_EMPTY_CONTENT_TEXT, FieldType.I18N, + this.emptyContentText); + } + outcome.putDataSetEntry(TaskViewConstants.FIELD_EMPTY_CONTENT_ICON, FieldType.TEXT, + this.emptyContentIcon); return outcome; } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java index 32e9b716f67..771f27726ce 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java @@ -13,4 +13,6 @@ public class TaskViewConstants extends ViewConstants { public static final String FIELD_ALLOW_HEADER_TABLE_MODE = "task_allow_header_table_mode"; public static final String FIELD_USE_DEFAULT_HEADERS = "use_task_default_headers"; public static final String FIELD_SHOW_MORE_MENU = "task_show_more_menu"; + public static final String FIELD_EMPTY_CONTENT_TEXT = "task_empty_content_text"; + public static final String FIELD_EMPTY_CONTENT_ICON = "task_empty_content_icon"; } diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index a017c3fce7e..f8a30347617 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -431,6 +431,41 @@ is_tabbed_var_arc Is tabbed + + case_empty_content_text + Empty content text + If empty, default text will be used + + + case_empty_content_icon + Empty content icon + If empty, default icon will be used + + 0 + + + icon: f.this, + iconPreview: f.case_empty_content_icon_preview; + + if (icon.value == "") { + change iconPreview value {"""]]>} + return + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + + + case_empty_content_icon_preview + Empty content icon preview + + htmltextarea + + @@ -543,7 +578,7 @@ view_dataGroup - 4 + 3 grid case_view_search_type @@ -555,7 +590,7 @@ 0 0 1 - 2 + 1 outline @@ -566,14 +601,56 @@ editable - 2 + 1 0 1 - 2 + 1 + + outline + + + + case_empty_content_text + + editable + + + 0 + 1 + 1 + 1 + + outline + + + + case_empty_content_icon + + editable + + + 1 + 1 + 1 + 1 outline + + case_empty_content_icon_preview + + visible + + + 2 + 1 + 1 + 1 + + standard + + advanced_content_form @@ -581,9 +658,9 @@ 0 - 1 + 2 1 - 4 + 3 outline @@ -1159,6 +1236,24 @@ visible + + case_empty_content_text + + visible + + + + case_empty_content_icon + + visible + + + + case_empty_content_icon_preview + + visible + + create_case_button_title diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 140c69c6379..8b35a669b23 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -486,6 +486,9 @@ tab_name Name of the item Will be shown in tab + + new com.netgrif.application.engine.petrinet.domain.I18nString("View", [sk: "Zobrazenie", de: "Die Ansicht"]) + view_configuration_type @@ -1444,7 +1447,7 @@ configuration_view - 4 + 3 grid use_custom_view @@ -1497,7 +1500,7 @@ 1 0 1 - 3 + 2 outline @@ -1531,27 +1534,13 @@ - - tab_name - - editable - - - 2 - 0 - 1 - 1 - - outline - - use_tab_icon editable - 3 + 2 0 1 1 @@ -1571,7 +1560,7 @@ - tab_icon + tab_name editable @@ -1584,16 +1573,30 @@ outline + + tab_icon + + editable + + + 1 + 1 + 1 + 1 + + outline + + tab_icon_preview visible - 1 + 2 1 1 - 2 + 1 standard @@ -1607,7 +1610,7 @@ 0 2 1 - 4 + 3 outline @@ -1654,7 +1657,7 @@ 0 3 1 - 4 + 3 outline diff --git a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml index c9f25059274..b8298bfa552 100644 --- a/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml @@ -323,6 +323,41 @@ + + task_empty_content_text + Empty content text + If empty, default text will be used + + + task_empty_content_icon + Empty content icon + If empty, default icon will be used + + 0 + + + icon: f.this, + iconPreview: f.task_empty_content_icon_preview; + + if (icon.value == "") { + change iconPreview value {"""]]>} + return + } + + change iconPreview value { + """]]> + icon.value + """]]> + } + + + + + + task_empty_content_icon_preview + Empty content icon preview + + htmltextarea + + @@ -419,7 +454,7 @@ view_dataGroup - 4 + 3 grid task_view_search_type @@ -431,7 +466,7 @@ 0 0 1 - 3 + 1 outline @@ -442,7 +477,7 @@ editable - 3 + 1 0 1 1 @@ -451,7 +486,7 @@ - advanced_content_form + task_empty_content_text editable @@ -459,7 +494,49 @@ 0 1 1 - 4 + 1 + + outline + + + + task_empty_content_icon + + editable + + + 1 + 1 + 1 + 1 + + outline + + + + task_empty_content_icon_preview + + visible + + + 2 + 1 + 1 + 1 + + standard + + + + advanced_content_form + + editable + + + 0 + 2 + 1 + 3 outline @@ -838,6 +915,24 @@ visible + + task_empty_content_text + + visible + + + + task_empty_content_icon + + visible + + + + task_empty_content_icon_preview + + visible + + task_is_header_mode_changeable From 8e6f3db68fd7007d1ccbcdd19d9766b4b68a674d Mon Sep 17 00:00:00 2001 From: chvostek Date: Thu, 14 May 2026 14:58:50 +0200 Subject: [PATCH 077/174] [ETASK-23] Dynamic view configuration - fix test - update doc --- .../engine/menu/service/MenuItemService.java | 10 ++++------ .../application/engine/menu/MenuItemServiceTest.java | 7 +++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 27a5f80c6c7..4fc58a05749 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -402,13 +402,11 @@ public Case removeChildItemFromParent(String folderId, Case childItem) { } /** - * Retrieves menu item data along with its associated view configuration data. The method collects immediate - * data from the menu item case and recursively traverses through all associated view configuration cases, - * aggregating their immediate data into a single map. - * todo 23 doc + * Retrieves menu item data groups for the specified case and locale. + * * @param caseId identifier of the menu item case - * @return map where keys are process identifiers (with "_configuration" suffix removed for view cases) and - * values are lists of immediate data fields from the corresponding cases + * @param locale locale to use for retrieving localized data + * @return list of data groups from the menu item case */ @Override public List getMenuItemData(String caseId, Locale locale) { diff --git a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java index c1df0ebdf78..a6c4d428352 100644 --- a/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java +++ b/src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java @@ -6,11 +6,11 @@ import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; +import com.netgrif.application.engine.petrinet.domain.DataGroup; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.startup.SuperCreator; import com.netgrif.application.engine.workflow.domain.Case; -import com.netgrif.application.engine.workflow.domain.eventoutcomes.dataoutcomes.GetDataGroupsEventOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -113,15 +113,14 @@ public void removeChildItemFromParentTest() { @Test public void getMenuItemDataTest() throws TransitionNotExecutableException { - // todo 23 fix test assertThrows(IllegalArgumentException.class, () -> menuItemService.getMenuItemData("wrongCaseId", Locale.getDefault())); Case menuItemCase = createDefaultMenuItem("my_menu_item", new I18nString("This is name", Map.of("sk", "Toto je nazov"))); login(); - GetDataGroupsEventOutcome result = menuItemService.getMenuItemData(menuItemCase.getStringId(), Locale.getDefault()); - assertTrue(result != null); + List result = menuItemService.getMenuItemData(menuItemCase.getStringId(), Locale.getDefault()); + assertTrue(result != null && !result.isEmpty()); } @Test From bfc90b15cd4824306aa1eaa9b765b7d3fa6441a6 Mon Sep 17 00:00:00 2001 From: chvostek Date: Fri, 15 May 2026 14:33:05 +0200 Subject: [PATCH 078/174] [ETASK-23] Dynamic view configuration - begin updating filter --- .../petriNets/engine-processes/filter.xml | 14 ++-- .../menu/case_view_configuration.xml | 81 +++++++++---------- 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/main/resources/petriNets/engine-processes/filter.xml b/src/main/resources/petriNets/engine-processes/filter.xml index 0be99e1940f..3e721c26fc5 100644 --- a/src/main/resources/petriNets/engine-processes/filter.xml +++ b/src/main/resources/petriNets/engine-processes/filter.xml @@ -121,27 +121,29 @@ i18n_filter_name - + </data> <data type="text"> <id>filter_case_id</id> - <title> + </data> <data type="text"> <id>menu_identifier</id> - <title> + </data> <data type="button"> <id>and_me</id> + <title/> <placeholder name="and">AND</placeholder> </data> <data type="button"> <id>and_view</id> + <title/> <placeholder name="and">AND</placeholder> </data> <data type="taskRef"> <id>taskref_and_parent</id> - <title> + </data> <data type="text"> <id>trimmed_origin_view_id</id> @@ -172,7 +174,7 @@ </data> <data type="enumeration_map"> <id>missing_nets_translation</id> - <title> + <options> <option key="sk">Zoznam chýbajúcich procesov pre aktuálny filter:</option> <option key="en">List of missing processes for current filter:</option> @@ -181,7 +183,7 @@ </data> <data type="taskRef"> <id>my_full_filter</id> - <title> + </data> <data type="multichoice_map"> <id>allowed_nets</id> diff --git a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml index f8a30347617..b93951a6913 100644 --- a/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml +++ b/src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml @@ -224,6 +224,39 @@ <allowedNet>filter</allowedNet> </allowedNets> </data> + <data type="enumeration_map"> + <id>available_filter_cases</id> + <title/> + <component> + <name>caseref</name> + <property key="headers">meta-title,meta-author,filter-visibility</property> + <property key="allowedNets">filter</property> + <property key="createCase">true</property> + <property key="search">true</property> + <property key="filter">true</property> + <property key="filterQuery">{ "query": "processIdentifier:filter AND (author:<<me>> OR dataSet.visibility.keyValue:public) AND dataSet.filter_type.keyValue:Case" }</property> + </component> + <event type="set"> + <id>0</id> + <actions phase="post"> + <action> + available_filter_cases: f.this, + current_filter_preview: f.current_filter_preview; + + if (available_filter_cases.value == null || available_filter_cases.value == "") { + change current_filter_preview value { [] } + return + } + change current_filter_preview value { + return [findTask({it.caseId.eq(available_filter_cases.value).and(it.transitionId.eq("view_filter"))}).stringId] + } + </action> + </actions> + </event> + <allowedNets> + <allowedNet>filter</allowedNet> + </allowedNets> + </data> <!-- VIEW CONFIGURATION DATA --> <data type="enumeration_map" immediate="true"> @@ -699,7 +732,7 @@ <cols>4</cols> <layout>grid</layout> <dataRef> - <id>filter_autocomplete_selection</id> + <id>available_filter_cases</id> <logic> <behavior>editable</behavior> </logic> @@ -707,48 +740,6 @@ <x>0</x> <y>0</y> <rows>1</rows> - <cols>3</cols> - <template>material</template> - <appearance>outline</appearance> - </layout> - </dataRef> - <dataRef> - <id>update_filter</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>3</x> - <y>0</y> - <rows>1</rows> - <cols>1</cols> - <template>material</template> - <appearance>standard</appearance> - </layout> - </dataRef> - <dataRef> - <id>selected_filter_preview</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>1</y> - <rows>1</rows> - <cols>4</cols> - <template>material</template> - <appearance>standard</appearance> - </layout> - </dataRef> - <dataRef> - <id>filter_header</id> - <logic> - <behavior>visible</behavior> - </logic> - <layout> - <x>0</x> - <y>2</y> - <rows>1</rows> <cols>4</cols> <template>material</template> <appearance>outline</appearance> @@ -761,7 +752,7 @@ </logic> <layout> <x>0</x> - <y>3</y> + <y>1</y> <rows>1</rows> <cols>4</cols> <template>material</template> @@ -1189,7 +1180,7 @@ <alignment>start</alignment> <stretch>true</stretch> <dataRef> - <id>filter_autocomplete_selection</id> + <id>available_filter_cases</id> <logic> <behavior>visible</behavior> </logic> From dfea0b87e42feb030c2e8badec1db1c2dbb52a0d Mon Sep 17 00:00:00 2001 From: chvostek <chvostek@netgrif.com> Date: Fri, 15 May 2026 15:58:10 +0200 Subject: [PATCH 079/174] [ETASK-23] Dynamic view configuration - introduce menu item configuration templates --- .../engine/menu/domain/templates/README.md | 48 +++++++++++++++++ .../templates/SimpleCaseViewTemplate.java | 47 +++++++++++++++++ .../templates/SimpleTaskViewTemplate.java | 47 +++++++++++++++++ .../templates/SingleTaskViewTemplate.java | 48 +++++++++++++++++ .../templates/TabbedCaseViewTemplate.java | 52 +++++++++++++++++++ .../templates/TabbedTaskViewTemplate.java | 47 +++++++++++++++++ .../templates/TabbedTicketViewTemplate.java | 47 +++++++++++++++++ .../menu/domain/templates/Template.java | 10 ++++ .../engine/menu/service/MenuItemService.java | 6 +++ .../menu/service/MenuItemTemplateHolder.java | 52 +++++++++++++++++++ .../service/interfaces/IMenuItemService.java | 1 + .../engine-processes/menu/menu_item.xml | 30 +++++++++++ 12 files changed, 435 insertions(+) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java create mode 100644 src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md b/src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md new file mode 100644 index 00000000000..7dd8af166fa --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md @@ -0,0 +1,48 @@ +# Menu Item Template Creation Guide + +## Overview + +Templates provide predefined configurations for menu items that can be reused throughout the application. Each template +encapsulates a complete menu item structure with its associated views and filters. + +## Template Structure + +A template must implement the `Template` interface with three core components: + +- **Identifier**: A unique string constant that identifies the template +- **Name**: An internationalized string (I18nString) providing the display name in multiple languages +- **Template Body**: A MenuItemBody instance containing the complete menu item configuration + +## Creation Steps + +1. **Create Template Class** + - Implement the `Template` interface + - Define a unique public static IDENTIFIER constant + - Create a private static I18nString NAME with translations + - Declare a private static MenuItemBody TEMPLATE field + +2. **Build Template Configuration** + - Implement a private static `buildTemplate()` method + - Instantiate MenuItemBody and configure its properties + - Create and configure the appropriate view body (CaseViewBody, TaskViewBody, ...) + - Set up FilterBody for data filtering requirements + - Configure chained views if needed (e.g., TaskViewBody within CaseViewBody) + - Wire all components together through setter methods + +3. **Implement Interface Methods** + - Return IDENTIFIER from `getIdentifier()` + - Return NAME from `getName()` + - Return TEMPLATE from `getTemplate()` + +4. **Register Template** + - Add the template to the `MenuItemTemplateHolder.templates` map + - Use the template's IDENTIFIER as the key + - Instantiate the template class as the value + +## Best Practices + +- Keep identifiers lowercase with underscores for consistency +- Provide translations for all supported languages in the NAME field +- Build template configuration statically to ensure immutability +- Ensure all required view and filter configurations are properly initialized +- Use descriptive identifiers that reflect the template's purpose diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java new file mode 100644 index 00000000000..1fc40d4e94f --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class SimpleCaseViewTemplate implements Template { + + public static final String IDENTIFIER = "simple_case_view"; + + private static final I18nString NAME = new I18nString("Simple case view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + CaseViewBody caseViewBody = new CaseViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + caseViewBody.setFilterBody(filterBody); + menuItemBody.setView(caseViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java new file mode 100644 index 00000000000..98df69c9bfb --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class SimpleTaskViewTemplate implements Template { + + public static final String IDENTIFIER = "simple_task_view"; + + private static final I18nString NAME = new I18nString("Simple task view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + TaskViewBody taskViewBody = new TaskViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + taskViewBody.setFilterBody(filterBody); + menuItemBody.setView(taskViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java new file mode 100644 index 00000000000..893b505b740 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java @@ -0,0 +1,48 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class SingleTaskViewTemplate implements Template { + + public static final String IDENTIFIER = "single_task_view"; + + private static final I18nString NAME = new I18nString("Single task view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + singleTaskViewBody.setFilterBody(filterBody); + menuItemBody.setView(singleTaskViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java new file mode 100644 index 00000000000..88656826c20 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java @@ -0,0 +1,52 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class TabbedCaseViewTemplate implements Template { + + public static final String IDENTIFIER = "tabbed_case_view"; + + private static final I18nString NAME = new I18nString("Tabbed case view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + CaseViewBody caseViewBody = new CaseViewBody(); + // todo 23 case view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + TaskViewBody taskViewBody = new TaskViewBody(); + // todo 23 task view body data + + caseViewBody.setFilterBody(filterBody); + caseViewBody.setChainedView(taskViewBody); + menuItemBody.setView(caseViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java new file mode 100644 index 00000000000..38be03aaae9 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.FilterBody; +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class TabbedTaskViewTemplate implements Template { + + public static final String IDENTIFIER = "tabbed_task_view"; + + private static final I18nString NAME = new I18nString("Tabbed task view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + TaskViewBody taskViewBody = new TaskViewBody(); + // todo 23 task view body data + + FilterBody filterBody = new FilterBody(); + // todo 23 filter body data + + taskViewBody.setFilterBody(filterBody); + menuItemBody.setView(taskViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java new file mode 100644 index 00000000000..3b1eb03e775 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody; +import com.netgrif.application.engine.menu.domain.configurations.TabbedTicketViewBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; + +public class TabbedTicketViewTemplate implements Template { + + public static final String IDENTIFIER = "tabbed_ticket_view"; + + private static final I18nString NAME = new I18nString("Tabbed ticket view", + Map.of("sk", "", "de", "")); // todo 23 translate + + private static final MenuItemBody TEMPLATE = buildTemplate(); + + private static MenuItemBody buildTemplate() { + MenuItemBody menuItemBody = new MenuItemBody(); + // todo 23 menu item body data + // is tabbed + + TabbedTicketViewBody tabbedTicketViewBody = new TabbedTicketViewBody(); + // todo 23 case view body data + + SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); + // todo 23 task view body data + + tabbedTicketViewBody.setChainedView(singleTaskViewBody); + menuItemBody.setView(tabbedTicketViewBody); + + return menuItemBody; + } + + public String getIdentifier() { + return IDENTIFIER; + } + + public I18nString getName() { + return NAME; + } + + public MenuItemBody getTemplate() { + return TEMPLATE; + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java new file mode 100644 index 00000000000..ecbc61f3ae3 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java @@ -0,0 +1,10 @@ +package com.netgrif.application.engine.menu.domain.templates; + +import com.netgrif.application.engine.menu.domain.MenuItemBody; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +public interface Template { + String getIdentifier(); + I18nString getName(); + MenuItemBody getTemplate(); +} diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index 4fc58a05749..acf42be17d8 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -415,6 +415,12 @@ public List<DataGroup> getMenuItemData(String caseId, Locale locale) { return dataService.getDataGroups(taskId, locale).getData(); } + // todo 23 doc + @Override + public void handleConfigurationTemplate(Case menuItemCase) { + // todo 23 + } + protected Case findCase(String processIdentifier, String query) { CaseSearchRequest request = CaseSearchRequest.builder() .process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier))) diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java new file mode 100644 index 00000000000..10070526211 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java @@ -0,0 +1,52 @@ +package com.netgrif.application.engine.menu.service; + +import com.netgrif.application.engine.menu.domain.templates.*; +import com.netgrif.application.engine.petrinet.domain.I18nString; + +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Holder class that manages predefined menu item templates. + * <p> + * This class provides a central registry for menu item templates that can be used + * throughout the application. Templates are identified by unique string identifiers + * and can be retrieved or transformed into user-selectable options. + * </p> + */ +public class MenuItemTemplateHolder { + /** + * Map of available menu item templates indexed by their unique identifiers. + * <p> + * Each entry maps a template identifier to its corresponding Template instance. + * This map is immutable and initialized with predefined templates. + * The key is the template identifier (String), and the value is the Template instance. + * </p> + */ + public static Map<String, Template> templates = Map.of( + TabbedCaseViewTemplate.IDENTIFIER, new TabbedCaseViewTemplate(), + TabbedTaskViewTemplate.IDENTIFIER, new TabbedTaskViewTemplate(), + SimpleCaseViewTemplate.IDENTIFIER, new SimpleCaseViewTemplate(), + SimpleTaskViewTemplate.IDENTIFIER, new SimpleTaskViewTemplate(), + SingleTaskViewTemplate.IDENTIFIER, new SingleTaskViewTemplate(), + TabbedTicketViewTemplate.IDENTIFIER, new TabbedTicketViewTemplate() + ); + + /** + * Transforms the available templates into a map of selectable options. + * <p> + * This method converts the templates map into a format suitable for displaying + * as user-selectable options, where the key is the template identifier and the + * value is the internationalized name of the template. + * </p> + * + * @return a map where keys are template identifiers and values are internationalized template names + */ + public static Map<String, I18nString> transformToOptions() { + return templates.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().getName() + )); + } +} diff --git a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java index 90cd699331b..5f0ef140ddf 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java @@ -31,6 +31,7 @@ public interface IMenuItemService { Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException; Case removeChildItemFromParent(String folderId, Case childItem); List<DataGroup> getMenuItemData(String caseId, Locale locale); + void handleConfigurationTemplate(Case menuItemCase); /** * Gets all tabbed or non-tabbed views diff --git a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml index 8b35a669b23..01ebb9eae98 100644 --- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml +++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml @@ -630,6 +630,13 @@ </actions> </event> </data> + <data type="enumeration_map"> + <id>configuration_templates</id> + <title name="todo 23">Pick a configuration template + + com.netgrif.application.engine.menu.service.MenuItemTemplateHolder.transformToOptions() + + @@ -812,6 +819,20 @@ outline + + configuration_templates + + editable + + + 0 + 2 + 1 + 4 + + outline + + finish @@ -827,6 +848,15 @@ changeMenuItem useCase uri { newUri } change name value { new com.netgrif.application.engine.petrinet.domain.I18nString(identifier.value) } + + configuration_templates: f.configuration_templates; + + if (configuration_templates.value == null || configuration_templates.value == "") { + return + } + + menuItemService.handleConfigurationTemplate(configuration_templates.value) + Create From 0d40b4b5689bbe3452d8a0be5191c2ec8c667b76 Mon Sep 17 00:00:00 2001 From: chvostek Date: Mon, 18 May 2026 11:33:44 +0200 Subject: [PATCH 080/174] [ETASK-23] Dynamic view configuration - finish implementation of template configurations --- .../domain/ConfigurationTemplateOutcome.java | 20 +++++++++ .../engine/menu/domain/MenuItemBody.java | 32 ++++++++++++-- .../engine/menu/domain/MenuItemConstants.java | 1 + .../templates/SimpleCaseViewTemplate.java | 11 +---- .../templates/SimpleTaskViewTemplate.java | 11 +---- .../templates/SingleTaskViewTemplate.java | 12 +---- .../templates/TabbedCaseViewTemplate.java | 17 ++----- .../templates/TabbedTaskViewTemplate.java | 12 ++--- .../templates/TabbedTicketViewTemplate.java | 7 +-- .../menu/domain/templates/Template.java | 25 +++++++++++ .../engine/menu/service/MenuItemService.java | 44 ++++++++++++++++--- .../menu/service/MenuItemTemplateHolder.java | 18 +++++++- .../service/interfaces/IMenuItemService.java | 3 +- .../engine-processes/menu/menu_item.xml | 9 ++-- 14 files changed, 148 insertions(+), 74 deletions(-) create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java b/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java new file mode 100644 index 00000000000..e31d6bbdbea --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java @@ -0,0 +1,20 @@ +package com.netgrif.application.engine.menu.domain; + +import java.util.HashMap; +import java.util.Map; + +public class ConfigurationTemplateOutcome { + // todo 23 doc + public final Map mapping; + + public ConfigurationTemplateOutcome() { + this.mapping = new HashMap<>(); + } + + public ConfigurationTemplateOutcome(ToDataSetOutcome toDataSetOutcome) { + this(); + toDataSetOutcome.getDataSet() + .forEach((fieldId, fieldMap) -> this.mapping.put(fieldId, fieldMap.get("value"))); + } + +} diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java index 15f73c3e8c0..bb19500b411 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java @@ -2,6 +2,7 @@ import com.netgrif.application.engine.menu.domain.configurations.ViewBody; import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; +import com.netgrif.application.engine.menu.domain.templates.Template; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.I18nString; import com.netgrif.application.engine.petrinet.domain.dataset.FieldType; @@ -31,7 +32,7 @@ public class MenuItemBody { private String customViewSelector; private boolean isAutoSelect = false; - private boolean useTabbedView; + private boolean useTabbedView = true; private String tabIcon; private boolean useTabIcon = true; private I18nString tabName; @@ -108,8 +109,14 @@ public void setTabName(String name) { } public void setView(ViewBody viewBody) { - this.view = viewBody; - this.useTabbedView = viewBody == null || viewBody.getViewType().isTabbed(); + if (viewBody != null) { + this.view = viewBody; + MenuItemView viewType = viewBody.getViewType(); + if (viewType.isTabbed() != viewType.isUntabbed()) { + // if isTabbed == isUntabbed we cannot determine the result value + this.useTabbedView = viewType.isTabbed(); + } + } } /** @@ -176,6 +183,24 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas outcome.putDataSetEntryOptions(MenuItemConstants.FIELD_ALLOWED_ROLES, FieldType.MULTICHOICE_MAP, this.allowedRoles); outcome.putDataSetEntryOptions(MenuItemConstants.FIELD_BANNED_ROLES, FieldType.MULTICHOICE_MAP, this.bannedRoles); + outcome = toDataSetWithView(viewCase, outcome); + + return outcome; + } + + /** + * Transforms minimal attributes into dataSet for view configuration by template. + * + * @param viewCase case instance of view + * @return {@link ToDataSetOutcome} object with dataSet containing only view-related fields + */ + public ToDataSetOutcome toDataSetByConfigTemplate(Case viewCase) { + ToDataSetOutcome outcome = new ToDataSetOutcome(); + outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TABBED_VIEW, FieldType.BOOLEAN, this.useTabbedView); + return toDataSetWithView(viewCase, outcome); + } + + protected ToDataSetOutcome toDataSetWithView(Case viewCase, ToDataSetOutcome outcome) { if (viewCase != null) { outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP, this.view.getViewType().getIdentifier()); @@ -186,7 +211,6 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas String allDataTaskId = MenuItemUtils.findTaskIdInCase(viewCase, ViewConstants.TRANS_ALL_MENU_DATA_ID); outcome.putDataSetEntry(MenuItemConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM, FieldType.TASK_REF, List.of(allDataTaskId)); } - return outcome; } } diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java index 320c0f08ebf..a8405551449 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java @@ -29,6 +29,7 @@ public class MenuItemConstants { public static final String FIELD_VIEW_CONFIGURATION_ID = "view_configuration_id"; public static final String FIELD_VIEW_CONFIGURATION_FORM = "view_configuration_form"; public static final String FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM = "view_configuration_all_data_form"; + public static final String FIELD_CONFIGURATION_TEMPLATES = "configuration_templates"; public static final String TRANS_SETTINGS_ID = "item_settings"; public static final String TRANS_INIT_ID = "system_initialize"; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java index 1fc40d4e94f..a23b243f7eb 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -18,16 +17,10 @@ public class SimpleCaseViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(false); CaseViewBody caseViewBody = new CaseViewBody(); - // todo 23 task view body data - - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - caseViewBody.setFilterBody(filterBody); + caseViewBody.setFilterBody(Template.defaultCaseFilterBody(NAME)); menuItemBody.setView(caseViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java index 98df69c9bfb..aa4815c2f6e 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -18,16 +17,10 @@ public class SimpleTaskViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(false); TaskViewBody taskViewBody = new TaskViewBody(); - // todo 23 task view body data - - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - taskViewBody.setFilterBody(filterBody); + taskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); menuItemBody.setView(taskViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java index 893b505b740..98ea5f60190 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java @@ -1,9 +1,7 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.SingleTaskViewBody; -import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; import java.util.Map; @@ -19,16 +17,10 @@ public class SingleTaskViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(false); SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); - // todo 23 task view body data - - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - singleTaskViewBody.setFilterBody(filterBody); + singleTaskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); menuItemBody.setView(singleTaskViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java index 88656826c20..609384ad046 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.CaseViewBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; @@ -13,26 +12,18 @@ public class TabbedCaseViewTemplate implements Template { public static final String IDENTIFIER = "tabbed_case_view"; private static final I18nString NAME = new I18nString("Tabbed case view", - Map.of("sk", "", "de", "")); // todo 23 translate + Map.of("sk", "Zobrazenie prípadov v záložkách", "de", "Registerkartenansicht für Fälle")); private static final MenuItemBody TEMPLATE = buildTemplate(); private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(true); CaseViewBody caseViewBody = new CaseViewBody(); - // todo 23 case view body data - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - TaskViewBody taskViewBody = new TaskViewBody(); - // todo 23 task view body data - - caseViewBody.setFilterBody(filterBody); - caseViewBody.setChainedView(taskViewBody); + caseViewBody.setFilterBody(Template.defaultCaseFilterBody(NAME)); + caseViewBody.setChainedView(new TaskViewBody()); menuItemBody.setView(caseViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java index 38be03aaae9..47877bf8c1c 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java @@ -1,6 +1,5 @@ package com.netgrif.application.engine.menu.domain.templates; -import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.menu.domain.configurations.TaskViewBody; import com.netgrif.application.engine.petrinet.domain.I18nString; @@ -12,22 +11,17 @@ public class TabbedTaskViewTemplate implements Template { public static final String IDENTIFIER = "tabbed_task_view"; private static final I18nString NAME = new I18nString("Tabbed task view", - Map.of("sk", "", "de", "")); // todo 23 translate + Map.of("sk", "Zobrazenie úloh v záložkách", "de", "Aufgabenansicht in Registerkarten")); private static final MenuItemBody TEMPLATE = buildTemplate(); private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(true); TaskViewBody taskViewBody = new TaskViewBody(); - // todo 23 task view body data - FilterBody filterBody = new FilterBody(); - // todo 23 filter body data - - taskViewBody.setFilterBody(filterBody); + taskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); menuItemBody.setView(taskViewBody); return menuItemBody; diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java index 3b1eb03e775..347b0d96cdb 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java @@ -18,15 +18,12 @@ public class TabbedTicketViewTemplate implements Template { private static MenuItemBody buildTemplate() { MenuItemBody menuItemBody = new MenuItemBody(); - // todo 23 menu item body data - // is tabbed + menuItemBody.setUseTabbedView(true); TabbedTicketViewBody tabbedTicketViewBody = new TabbedTicketViewBody(); - // todo 23 case view body data SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody(); - // todo 23 task view body data - + singleTaskViewBody.setFilterBody(Template.defaultTaskFilterBody(NAME)); tabbedTicketViewBody.setChainedView(singleTaskViewBody); menuItemBody.setView(tabbedTicketViewBody); diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java index ecbc61f3ae3..c6c2a462842 100644 --- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java +++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java @@ -1,10 +1,35 @@ package com.netgrif.application.engine.menu.domain.templates; +import com.netgrif.application.engine.menu.domain.FilterBody; import com.netgrif.application.engine.menu.domain.MenuItemBody; import com.netgrif.application.engine.petrinet.domain.I18nString; +import java.util.List; + public interface Template { String getIdentifier(); I18nString getName(); MenuItemBody getTemplate(); + + static FilterBody defaultTaskFilterBody(I18nString name) { + FilterBody filterBody = new FilterBody(); + filterBody.setIcon("filter"); + filterBody.setType("Task"); + filterBody.setVisibility("private"); + filterBody.setTitle(name); + filterBody.setQuery("*"); + filterBody.setAllowedNets(List.of()); + return filterBody; + } + + static FilterBody defaultCaseFilterBody(I18nString name) { + FilterBody filterBody = new FilterBody(); + filterBody.setIcon("filter"); + filterBody.setType("Case"); + filterBody.setVisibility("private"); + filterBody.setTitle(name); + filterBody.setQuery("*"); + filterBody.setAllowedNets(List.of()); + return filterBody; + } } diff --git a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java index acf42be17d8..f7626296cfd 100644 --- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java +++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java @@ -5,12 +5,10 @@ import com.netgrif.application.engine.auth.service.interfaces.IUserService; import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; -import com.netgrif.application.engine.menu.domain.FilterBody; -import com.netgrif.application.engine.menu.domain.MenuItemBody; -import com.netgrif.application.engine.menu.domain.MenuItemConstants; -import com.netgrif.application.engine.menu.domain.ToDataSetOutcome; +import com.netgrif.application.engine.menu.domain.*; import com.netgrif.application.engine.menu.domain.configurations.ViewBody; import com.netgrif.application.engine.menu.domain.configurations.ViewConstants; +import com.netgrif.application.engine.menu.domain.templates.Template; import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService; import com.netgrif.application.engine.menu.utils.MenuItemUtils; import com.netgrif.application.engine.petrinet.domain.DataGroup; @@ -415,10 +413,42 @@ public List getMenuItemData(String caseId, Locale locale) { return dataService.getDataGroups(taskId, locale).getData(); } - // todo 23 doc + /** + * Handles the application of a configuration template to a menu item case. + *

+ * This method retrieves the selected configuration template from the menu item case, + * loads the corresponding template definition, and applies it by creating or updating + * the associated view configuration. If no template is selected, the method returns + * without making any changes. + *

+ * + * @param menuItemCase the menu item case to which the configuration template should be applied + * @return a ConfigurationTemplateOutcome containing the dataSet outcome from applying the template, + * or an empty outcome if no template was selected + * @throws TransitionNotExecutableException if the workflow transition required for applying the template configuration cannot be executed + * @throws IllegalArgumentException if the selected template identifier does not correspond to any registered template + */ @Override - public void handleConfigurationTemplate(Case menuItemCase) { - // todo 23 + public ConfigurationTemplateOutcome handleConfigurationTemplate(Case menuItemCase) throws TransitionNotExecutableException { + String selectedTemplate = (String) menuItemCase.getFieldValue(MenuItemConstants.FIELD_CONFIGURATION_TEMPLATES); + if (selectedTemplate == null || selectedTemplate.isEmpty()) { + return new ConfigurationTemplateOutcome(); + } + + String menuItemIdentifier = (String) menuItemCase.getFieldValue(MenuItemConstants.FIELD_IDENTIFIER); + log.debug("Handling configuration template selection for menu item: [{}, {}] and configuration template: {}", + menuItemCase.getStringId(), menuItemIdentifier, selectedTemplate); + Optional