changeMenuItem item uri { "/my_node1/my_node2" }
*
changeMenuItem item title { new I18nString("New title") }
*
changeMenuItem item title { "New title" }
*
changeMenuItem item menuIcon { "filter_alt" }
*
changeMenuItem item tabIcon { "filter_none" }
*
- * @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 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 itemAllowedRoles = [:],
Map itemBannedRoles = [:], List itemCaseDefaultHeaders = [],
List 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)
+ 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 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)
+
+ 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 caseDefaultHeaders = null,
+ List 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 caseDefaultHeaders = null,
+ List 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 "/my_node" to
* "/my_node/my_node2"
*
- * @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. "/my_new_node"
* */
void moveMenuItem(Case item, String destUri) {
- if (isCyclicNodePath(item, destUri)) {
- throw new IllegalArgumentException("Cyclic path not supported. Destination path: ${destUri}")
- }
-
- List casesToSave = new ArrayList<>()
-
- List parentIdList = item.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_PARENT_ID.attributeId].value as ArrayList
- 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 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 updateNodeInChildrenFoldersRecursive(Case parentFolder) {
- List childItemIds = parentFolder.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as List
- if (childItemIds == null || childItemIds.isEmpty()) {
- return new ArrayList()
- }
-
- List children = workflowService.findAllById(childItemIds)
-
- List 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 childIds = folderCase.dataSet[MenuItemConstants.PREFERENCE_ITEM_FIELD_CHILD_ITEM_IDS.attributeId].value as ArrayList
- 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, null 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 collectRolesForPreferenceItem(Map roles) {
Map temp = [:]
return roles.collectEntries { entry ->
- if (entry.value.equals(GLOBAL_ROLE)) {
+ if (entry.value == GLOBAL_ROLE) {
Set 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 allowedNets, Map roles = [:], Map bannedRoles = [:], Case group = null, List 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 allowedNets,
+ Map roles = [:], Map bannedRoles = [:], Case group = null,
+ List 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 allowedRoles = [:], Map bannedRoles = [:],
List caseDefaultHeaders = [], List 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 filterAllowedNets = [],
String itemAndFilterIcon = "filter_none", Map 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)
+ 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 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)
+ 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 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)
+
+ 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 createPreferenceItemNet() {
- importProcess("Petri net for filter preferences", PREFERRED_ITEM_NET_IDENTIFIER, PREFERRED_ITEM_FILE_NAME)
+ Optional createMenuItemNet() {
+ importProcess("Petri net for filter preferences", MENU_NET_IDENTIFIER, MENU_ITEM_FILE_NAME)
+ }
+
+ 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)
+ }.collect()
}
Optional 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 allowedNets;
+ private String icon;
+ private String visibility;
+ private Map metadata;
+
+ public FilterBody(Case filterCase) {
+ this.filter = filterCase;
+ }
+
+ /**
+ * todo javadoc
+ * */
+ public static Map getDefaultMetadata(String type) {
+ Map 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 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 allowedRoles;
+ private Map 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 allowedAssociatedViews;
+ private final boolean isTabbed;
+
+ MenuItemView(I18nString name, String identifier, List 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 findAllByIsTabbed(boolean isTabbed) {
+ return Arrays.stream(MenuItemView.values())
+ .filter(view -> view.isTabbed == isTabbed)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * todo javadoc
+ * */
+ 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/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> dataSet;
+ /**
+ * todo javadoc
+ * */
+ private ToDataSetOutcome associatedOutcome;
+
+ public ToDataSetOutcome() {
+ this.dataSet = new HashMap<>();
+ }
+
+ public ToDataSetOutcome(Map> dataSet) {
+ this.dataSet = dataSet;
+ }
+
+ /**
+ * todo javadoc
+ * */
+ public void putDataSetEntry(String fieldId, FieldType fieldType, @Nullable Object fieldValue) {
+ Map 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 options) {
+ Map 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 headersMode = new ArrayList<>(List.of("sort", "edit", "search"));
+ private String headersDefaultMode = "sort";
+ private List 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 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 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 casesToSave = new ArrayList<>();
+
+ List 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 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> 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 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 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> 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 updateNodeInChildrenFoldersRecursive(Case parentFolder) {
+ List childItemIds = MenuItemUtils.getCaseIdsFromCaseRef(parentFolder, MenuItemConstants.FIELD_CHILD_ITEM_IDS);
+ if (childItemIds == null || childItemIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ List children = workflowService.findAllById(childItemIds);
+
+ List 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> 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)));
+ } 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 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> dataSet = new HashMap<>();
+ appendChildCaseIdInDataSet(folderCase, childItemCaseId, dataSet);
+ return setData(folderCase, MenuItemConstants.TRANS_SYNC_ID, dataSet);
+ }
+
+ protected void addConfigurationIntoDataSet(Case configurationCase, Map> 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> dataSet) {
+ String taskId = MenuItemUtils.findTaskIdInCase(useCase, transId);
+ return setData(taskId, dataSet);
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ protected Case setData(String taskId, Map> dataSet) {
+ 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();
+ }
+
+}
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 getAvailableViewsAsOptions(boolean isTabbed) {
+ return MenuItemView.findAllByIsTabbed(isTabbed).stream()
+ .collect(Collectors.toMap(MenuItemView::getIdentifier, MenuItemView::getName));
+ }
+
+ /**
+ * todo javadoc
+ * */
+ default Map 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 getCaseIdsFromCaseRef(Case useCase, String caseRefId) {
+ try {
+ return (List) useCase.getFieldValue(caseRefId);
+ } catch (NullPointerException ignore) {
+ return null;
+ }
+ }
+
+ /**
+ * todo javadoc
+ * */
+ public static String getCaseIdFromCaseRef(Case useCase, String caseRefId) {
+ List 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 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 allowedRoles;
- private Map 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 caseHeadersMode = new ArrayList<>(List.of("sort", "edit", "search"));
- private String caseHeadersDefaultMode = "sort";
- private List 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 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());
- putDataSetEntry(dataSet, MenuItemConstants.PREFERENCE_ITEM_FIELD_CONTAINS_FILTER, FieldType.BOOLEAN, true);
- }
- 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);
- 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 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 @@
+
+ menu_item
+ MNI
+ 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
+
+
+ menu_item
+
+
+
+ move_previous_dest_uri
+
+
+
+ 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
+
+ Add
+
+ raised
+
+
+ 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 }
+
+
+
+ duplicate_new_title
+ Title of duplicated view
+
+
+ duplicate_view_identifier
+ View identifier
+ Must be unique
+
+
+ childItemIds
+
+
+ menu_item
+
+
+
+ childItemForms
+
+
+
+ hasChildren
+
+
+
+ duplicate_reset_childItemIds
+
+
+ hasChildren: f.hasChildren,
+ childItemIds: f.childItemIds;
+
+ change childItemIds value { [] }
+ change hasChildren value { false }
+
+
+
+ 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
+
+ Allow view for roles
+
+ 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}
+
+
+
+ remove_allowed_roles
+
+ Remove from allowed roles
+
+ 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}
+
+
+
+ add_banned_roles
+
+ Ban view for roles
+
+ 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}
+
+
+
+ remove_banned_roles
+
+ Remove from banned roles
+
+ 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 }
+
+
+
+ processes_available
+ 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
+
+
+ tabbed_case_view_configuration
+ tabbed_task_view_configuration
+
+
+
+ view_configuration_form
+
+
+
+ order_down
+
+ south
+
+ icon
+ true
+
+
+ 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"
+ ]
+ ])
+
+
+
+ order_up
+
+ north
+
+ icon
+ true
+
+
+ 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"
+ ]
+ ])
+
+
+
+
+
+ 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
+ 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
+ Použiť vlastné zobrazenie?
+ Konfiguračný identifikátor vlastného zobrazenia
+ Napríklad: "demo-tabbed-views"
+ Nastavenie položky
+ Roly
+ Presunúť položku
+ Presunúť
+ Duplikovať položku
+ Duplikovať
+ 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
+ URI položky
+ Nový uzol
+ 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
+ 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
+ Material Ikone ID. Liste den Ikonen mit IDs ist online verfügbar.
+ Beispiel: "demo-tabbed-views"
+ Rollen
+ Zulässige Rollen
+ Allgemein
+ Identifikationsnummer des Menüeintrages
+ 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
+ Eigener Ansicht anwenden?
+ Konfigurationsidentifikator der eigenen Ansicht
+ Menüeintrageinstellungen
+ Menüeintrag verschieben
+ verschieben
+ Menüeintrag duplizieren
+ duplizieren
+ 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
+ Automatische Anzeigeauswahl
+ Wenn ausgewählt, wird die Ansicht automatisch geöffnet
+
+
+
+
+ initialize
+ 340
+ 220
+
+ hourglass_empty
+
+ admin
+
+ true
+ true
+ true
+ true
+
+
+
+
+ data_sync
+ 340
+ 340
+
+
+ system
+
+ true
+
+
+
+
+
+ item_settings
+ 460
+ 100
+
+ settings
+ auto
+
+ admin
+
+ true
+ true
+ true
+ true
+
+
+
+ pre_general
+ 4
+ grid
+
+ menu_item_identifier
+
+ visible
+
+
+ 0
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ nodePath
+
+ visible
+
+
+ 2
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+
+ general_0
+ 4
+ grid
+ General
+
+ menu_name
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ menu_icon
+
+ editable
+
+
+ 2
+ 0
+ 1
+ 1
+ material
+ outline
+
+
+
+ menu_icon_preview
+
+ visible
+
+
+ 3
+ 0
+ 1
+ 1
+ material
+ standard
+
+
+
+ is_auto_select
+
+ editable
+
+
+ 1
+ 2
+ 1
+ 1
+ material
+ standard
+
+
+
+
+ roles_management
+ 5
+ grid
+ Roles
+
+ processes_available
+
+ editable
+
+
+ 0
+ 0
+ 2
+ 1
+ 0
+ material
+ outline
+
+
+
+ roles_available
+
+ editable
+
+
+ 1
+ 0
+ 2
+ 1
+ 0
+ material
+ outline
+
+
+
+ add_allowed_roles
+
+ editable
+
+
+ 2
+ 0
+ 1
+ 1
+ 0
+ material
+
+
+
+ allowed_roles
+
+ editable
+
+
+ 3
+ 0
+ 1
+ 1
+ 0
+ material
+ outline
+
+
+
+ remove_allowed_roles
+
+ editable
+
+
+ 4
+ 0
+ 1
+ 1
+ 0
+ material
+
+
+
+ add_banned_roles
+
+ editable
+
+
+ 2
+ 1
+ 1
+ 1
+ 0
+ material
+
+
+
+ banned_roles
+
+ editable
+
+
+ 3
+ 1
+ 1
+ 1
+ 0
+ material
+ outline
+
+
+
+ remove_banned_roles
+
+ editable
+
+
+ 4
+ 1
+ 1
+ 1
+ 0
+ material
+
+
+
+
+ configuration_view
+ 4
+ grid
+
+ View configuration
+
+ use_custom_view
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 1
+ material
+ 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
+ material
+ outline
+
+
+
+ use_tabbed_view
+
+ editable
+
+
+ 1
+ 0
+ 1
+ 1
+ material
+ 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
+ material
+ outline
+
+
+
+ use_tab_icon
+
+ hidden
+
+
+ 3
+ 0
+ 1
+ 1
+ 0
+ material
+
+
+ 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
+ material
+ outline
+
+
+
+ tab_icon_preview
+
+ hidden
+
+
+ 1
+ 1
+ 1
+ 2
+ material
+ standard
+
+
+
+ view_configuration_type
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 4
+ material
+ 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
+ material
+ 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
+ material
+ outline
+
+
+
+ move_dest_uri_new_node
+
+ editable
+
+
+ 2
+ 0
+ 1
+ 1
+ material
+ outline
+
+
+
+ move_add_node
+
+ editable
+
+
+ 3
+ 0
+ 1
+ 1
+ material
+ 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
+ material
+ outline
+
+
+
+ duplicate_view_identifier
+
+ editable
+ required
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ 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
+ material
+ 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
+ material
+ outline
+
+
+
+ menu_name_as_visible
+
+ visible
+
+
+ 2
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ order_down
+
+ editable
+
+
+ 4
+ 0
+ 1
+ 1
+ 1
+ material
+ outline
+
+
+
+ order_up
+
+ editable
+
+
+ 5
+ 0
+ 1
+ 1
+ 1
+ material
+ 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
+
+
+
+ current_filter_preview
+
+
+
+ filter_header
+
+ Current filter
+
+ divider
+
+
+
+ new_filter_id
+
+
+
+ contains_filter
+
+ false
+
+
+ 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
+
+ Update view with selected filter
+
+ raised
+
+
+ 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 }
+
+
+
+ filter_case
+
+
+ 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 }
+
+
+ filter
+
+
+
+
+
+ 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
+
+ Case view
+
+ divider
+
+
+
+ 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
+
+
+
+
+
+
+
+
+ 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
+
+
+ tabbed_task_view_configuration
+
+
+
+ view_configuration_form
+
+
+
+
+// todo prejst preklady
+
+ 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
+ Aktualizovať zobrazenie s vybraným filtrom
+ 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
+ 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
+ Filter
+ Súčasný filter
+ Zobrazenie prípadov
+ Všeobecné
+
+
+ Schaltflächentitel "Neuer Fall"
+ Ikone ID
+ Ikonevorschau
+ Anzuzeigende Attributmenge auswählen
+ Neue Filter auswählen
+ Beispiel: "meta-title,meta-visualId"
+ Versteckt
+ Einfacher Suchmodus
+ Sortieren
+ Suchen
+ Bearbeiten
+ Kopfzeilenmodus
+ Standardkopfzeilenmodus
+ Erlaube Änderung des Kopfzeilenmodus?
+ Erlaube Tabellenmodus?
+ Eigene Kopfzeilen verwenden?
+ Filter
+ Aktueller Filter
+ Allgemein
+ Aktualisiere die Ansicht mit dem ausgewählten Filter
+ 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
+ Menüeintrageinstellungen
+ Fallansicht
+
+
+
+ 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
+ material
+ outline
+
+
+
+
+ filter_update
+ 4
+ grid
+ Filter
+
+ filter_autocomplete_selection
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 3
+ material
+ outline
+
+
+
+ update_filter
+
+ visible
+
+
+ 3
+ 0
+ 1
+ 1
+ material
+ standard
+
+
+
+ selected_filter_preview
+
+ visible
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ standard
+
+
+
+
+ current_filter
+ 4
+ grid
+
+ filter_header
+
+ visible
+
+
+ 0
+ 0
+ 1
+ 4
+ material
+ outline
+
+
+
+ current_filter_preview
+
+ visible
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ standard
+
+
+
+
+ view_dataGroup
+ 4
+ grid
+
+ view_search_type
+
+ editable
+ required
+
+
+ 0
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ show_create_case_button
+
+ editable
+ required
+
+
+ 2
+ 0
+ 1
+ 1
+ material
+ outline
+
+
+
+ show_more_menu
+
+ editable
+ required
+
+
+ 3
+ 0
+ 1
+ 1
+ material
+ outline
+
+
+
+ create_case_button_title
+
+ editable
+
+
+ 0
+ 1
+ 1
+ 1
+ 0
+ material
+ outline
+
+
+
+ create_case_button_icon
+
+ editable
+
+
+ 1
+ 1
+ 1
+ 1
+ 0
+ material
+ outline
+
+
+
+ create_case_button_icon_preview
+
+ visible
+
+
+ 2
+ 1
+ 1
+ 1
+ 0
+ material
+ standard
+
+
+
+ require_title_in_creation
+
+ editable
+
+
+ 3
+ 1
+ 1
+ 1
+ 0
+ material
+ standard
+
+
+
+ banned_nets_in_creation
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 4
+ 0
+ material
+ 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
+ material
+ outline
+
+
+
+ allow_header_table_mode
+
+ editable
+ required
+
+
+ 1
+ 0
+ 1
+ 1
+ 0
+ material
+ outline
+
+
+
+ headers_mode
+
+ editable
+ required
+
+
+ 2
+ 0
+ 1
+ 2
+ 0
+ material
+ outline
+
+
+
+ headers_default_mode
+
+ editable
+ required
+
+
+ 4
+ 0
+ 1
+ 1
+ 0
+ material
+ 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
+ material
+ outline
+
+
+
+ default_headers
+
+ editable
+
+
+ 1
+ 1
+ 1
+ 4
+ 0
+ material
+ outline
+
+
+
+
+ associated_view
+ 4
+ grid
+
+ Next view
+
+ view_configuration_type
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 4
+ 0
+ material
+ 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
+ material
+ 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
+
+
+
+ current_filter_preview
+
+
+
+ filter_header
+
+ Current filter
+
+ divider
+
+
+
+ new_filter_id
+
+
+
+ contains_filter
+
+ false
+
+
+ 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
+
+ Update view with selected filter
+
+ raised
+
+
+ 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 }
+
+
+
+ filter_case
+
+
+ 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 }
+
+
+ filter
+
+
+
+ remove_filter
+
+ Remove filter
+
+ raised
+
+
+ filterCase: f.filter_case;
+
+ change filterCase value { [] }
+
+
+
+ merge_filters
+ Merge with base filter?
+ true
+
+
+
+
+ view_header
+
+ Task view
+
+ divider
+
+
+
+ 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
+
+
+
+
+
+
+
+ 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
+ material
+ outline
+
+
+
+
+ filter_update
+ 4
+ grid
+ Filter
+
+ filter_autocomplete_selection
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 3
+ material
+ outline
+
+
+
+ update_filter
+
+ visible
+
+
+ 3
+ 0
+ 1
+ 1
+ material
+ standard
+
+
+
+ selected_filter_preview
+
+ visible
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ standard
+
+
+
+
+ current_additional_filter
+ 4
+ grid
+
+ filter_header
+
+ visible
+
+
+ 0
+ 0
+ 1
+ 4
+ material
+ outline
+
+
+
+ current_filter_preview
+
+ visible
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ standard
+
+
+
+ merge_filters
+
+ hidden
+
+
+ 0
+ 2
+ 1
+ 1
+ material
+ standard
+
+
+
+ remove_filter
+
+ hidden
+
+
+ 1
+ 2
+ 1
+ 1
+ material
+ standard
+
+
+
+
+ view_dataGroup
+ 4
+ grid
+
+ view_search_type
+
+ editable
+ required
+
+
+ 0
+ 0
+ 1
+ 3
+ material
+ outline
+
+
+
+ show_more_menu
+
+ editable
+ required
+
+
+ 3
+ 0
+ 1
+ 1
+ material
+ 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
+ material
+ outline
+
+
+
+ allow_header_table_mode
+
+ editable
+ required
+
+
+ 1
+ 0
+ 1
+ 1
+ material
+ outline
+
+
+
+ headers_mode
+
+ editable
+ required
+
+
+ 2
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ headers_default_mode
+
+ editable
+ required
+
+
+ 4
+ 0
+ 1
+ 1
+ material
+ 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
+ material
+ outline
+
+
+
+ default_headers
+
+ editable
+
+
+ 1
+ 1
+ 1
+ 4
+ 0
+ material
+ 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
-
-
- preference_item
-
-
-
- move_previous_dest_uri
-
-
-
- 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
-
- Add
-
- raised
-
-
- 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 }
-
-
-
- duplicate_new_title
- Title of duplicated view
-
-
- duplicate_view_identifier
- View identifier
- Must be unique
-
-
- childItemIds
-
-
- preference_item
-
-
-
- childItemForms
-
-
-
- hasChildren
-
-
-
- duplicate_reset_childItemIds
-
-
- hasChildren: f.hasChildren,
- childItemIds: f.childItemIds;
-
- change childItemIds value { [] }
- change hasChildren value { false }
-
-
-
- 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
-
- Allow view for roles
-
- 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}
-
-
-
- remove_allowed_roles
-
- Remove from allowed roles
-
- 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}
-
-
-
- add_banned_roles
-
- Ban view for roles
-
- 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}
-
-
-
- remove_banned_roles
-
- Remove from banned roles
-
- 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 }
-
-
-
- processes_available
- 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
-
-
-
- current_filter_preview
-
-
-
- filter_header
-
- Current filter
-
- divider
-
-
-
- new_filter_id
-
-
-
- contains_filter
-
- false
-
-
- 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
-
- Update view with selected filter
-
- raised
-
-
- 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 }
-
-
-
- filter_case
-
-
- 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 { [] }
-
-
- filter
-
-
-
- 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
-
- Case view
-
- divider
-
-
-
- case_view_settings_taskRef
-
- case_view_settings
-
-
- 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
-
-
-
-
-
-
-
-
- case_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
-
-
-
- current_additional_filter_preview
-
-
-
- additional_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
-
- Update view with selected filter
-
- raised
-
-
- 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 }
-
-
-
- remove_additional_filter
-
- Remove additional filter
-
- raised
-
-
- filterCase: f.additional_filter_case;
-
- change filterCase value { [] }
-
-
-
- additional_filter_case
-
-
- 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 }
-
-
- filter
-
-
-
- merge_filters
- Merge with base filter?
- true
-
-
- task_view_settings_taskRef
-
- task_view_settings
-
-
- task_view_header
-
- Task view
-
- divider
-
-
-
- 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
-
-
-
-
-
-
-
- task_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
-
- south
-
- icon
- true
-
-
- 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"
- ]
- ])
-
-
-
- order_up
-
- north
-
- icon
- true
-
-
- 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"
- ]
- ])
-
-
-
-
-
- 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ť
- Automatické zvolenie zobrazenia
- Po automatickom zvolení sa dané zobrazenie používateľovi otvorí
-
-
- 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
- Automatische Anzeigeauswahl
- Wenn ausgewählt, wird die Ansicht automatisch geöffnet
-
-
-
-
- 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
- material
- outline
-
-
-
- nodePath
-
- visible
-
-
- 2
- 0
- 1
- 2
- material
- outline
-
-
-
-
- general_0
- 4
- grid
- General
-
- menu_name
-
- editable
-
-
- 0
- 0
- 1
- 2
- material
- outline
-
-
-
- menu_icon
-
- editable
-
-
- 2
- 0
- 1
- 1
- material
- outline
-
-
-
- menu_icon_preview
-
- visible
-
-
- 3
- 0
- 1
- 1
- material
- standard
-
-
-
- tab_name
-
- editable
-
-
- 0
- 1
- 1
- 1
- material
- outline
-
-
-
- use_tab_icon
-
- editable
-
-
- 1
- 1
- 1
- 1
- 0
- material
-
-
- 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
- material
- outline
-
-
-
- tab_icon_preview
-
- visible
-
-
- 3
- 1
- 1
- 1
- material
- standard
-
-
-
- is_auto_select
-
- editable
-
-
- 0
- 2
- 1
- 1
- material
- standard
-
-
-
- use_custom_view
-
- editable
-
-
- 1
- 2
- 1
- 1
- material
- 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
- material
- outline
-
-
-
-
- roles_management
- 5
- grid
- Roles
-
- processes_available
-
- editable
-
-
- 0
- 0
- 2
- 1
- 0
- material
- outline
-
-
-
- roles_available
-
- editable
-
-
- 1
- 0
- 2
- 1
- 0
- material
- outline
-
-
-
- add_allowed_roles
-
- editable
-
-
- 2
- 0
- 1
- 1
- 0
- material
-
-
-
- allowed_roles
-
- editable
-
-
- 3
- 0
- 1
- 1
- 0
- material
- outline
-
-
-
- remove_allowed_roles
-
- editable
-
-
- 4
- 0
- 1
- 1
- 0
- material
-
-
-
- add_banned_roles
-
- editable
-
-
- 2
- 1
- 1
- 1
- 0
- material
-
-
-
- banned_roles
-
- editable
-
-
- 3
- 1
- 1
- 1
- 0
- material
- outline
-
-
-
- remove_banned_roles
-
- editable
-
-
- 4
- 1
- 1
- 1
- 0
- material
-
-
-
-
- filter_update
- 4
- grid
- Filter
-
- filter_autocomplete_selection
-
- editable
-
-
- 0
- 0
- 1
- 3
- material
- outline
-
-
-
- update_filter
-
- visible
-
-
- 3
- 0
- 1
- 1
- material
- standard
-
-
-
- selected_filter_preview
-
- visible
-
-
- 0
- 1
- 1
- 4
- material
- standard
-
-
-
-
- current_filter
- 4
- grid
-
- filter_header
-
- visible
-
-
- 0
- 0
- 1
- 4
- material
- outline
-
-
-
- current_filter_preview
-
- visible
-
-
- 0
- 1
- 1
- 4
- material
- standard
-
-
-
-
- case_view_settings_dataGroup
- 4
- grid
-
- case_view_header
-
- hidden
-
-
- 0
- 0
- 1
- 4
- material
- outline
-
-
-
- case_view_settings_taskRef
-
- hidden
-
-
- 0
- 1
- 1
- 4
- material
- outline
-
-
-
-
- task_view_settings_dataGroup
- 4
- grid
-
- task_view_header
-
- hidden
-
-
- 0
- 0
- 1
- 4
- material
- outline
-
-
-
- task_view_settings_taskRef
-
- hidden
-
-
- 0
- 1
- 1
- 4
- material
- 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
- material
- outline
-
-
-
- move_dest_uri_new_node
-
- editable
-
-
- 2
- 0
- 1
- 1
- material
- outline
-
-
-
- move_add_node
-
- editable
-
-
- 3
- 0
- 1
- 1
- material
- 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
- material
- outline
-
-
-
- duplicate_view_identifier
-
- editable
- required
-
-
- 0
- 1
- 1
- 4
- material
- 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
- material
- outline
-
-
-
- show_create_case_button
-
- editable
- required
-
-
- 2
- 0
- 1
- 1
- material
- outline
-
-
-
- case_show_more_menu
-
- editable
- required
-
-
- 3
- 0
- 1
- 1
- material
- outline
-
-
-
- create_case_button_title
-
- editable
-
-
- 0
- 1
- 1
- 1
- 0
- material
- outline
-
-
-
- create_case_button_icon
-
- editable
-
-
- 1
- 1
- 1
- 1
- 0
- material
- outline
-
-
-
- create_case_button_icon_preview
-
- visible
-
-
- 2
- 1
- 1
- 1
- 0
- material
- standard
-
-
-
- case_require_title_in_creation
-
- editable
-
-
- 3
- 1
- 1
- 1
- 0
- material
- standard
-
-
-
- case_banned_nets_in_creation
-
- editable
-
-
- 0
- 2
- 1
- 4
- 0
- material
- 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
- material
- outline
-
-
-
- case_allow_header_table_mode
-
- editable
- required
-
-
- 1
- 0
- 1
- 1
- 0
- material
- outline
-
-
-
- case_headers_mode
-
- editable
- required
-
-
- 2
- 0
- 1
- 2
- 0
- material
- outline
-
-
-
- case_headers_default_mode
-
- editable
- required
-
-
- 4
- 0
- 1
- 1
- 0
- material
- 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
- material
- outline
-
-
-
- case_default_headers
-
- editable
-
-
- 1
- 1
- 1
- 4
- 0
- material
- outline
-
-
-
-
-
-
- task_view_settings
- 340
- 340
-
-
- system
-
- true
-
-
-
- task_view_dataGroup
- 4
- grid
-
- task_view_search_type
-
- editable
- required
-
-
- 0
- 0
- 1
- 3
- material
- outline
-
-
-
- task_show_more_menu
-
- editable
- required
-
-
- 3
- 0
- 1
- 1
- material
- 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
- material
- outline
-
-
-
- task_allow_header_table_mode
-
- editable
- required
-
-
- 1
- 0
- 1
- 1
- material
- outline
-
-
-
- task_headers_mode
-
- editable
- required
-
-
- 2
- 0
- 1
- 2
- material
- outline
-
-
-
- task_headers_default_mode
-
- editable
- required
-
-
- 4
- 0
- 1
- 1
- material
- 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
- material
- outline
-
-
-
- task_default_headers
-
- editable
-
-
- 1
- 1
- 1
- 4
- 0
- material
- outline
-
-
-
-
- additional_filter_update
- 4
- grid
- Additional filter
-
- additional_filter_autocomplete_selection
-
- editable
-
-
- 0
- 0
- 1
- 3
- material
- outline
-
-
-
- update_additional_filter
-
- editable
-
-
- 3
- 0
- 1
- 1
- material
- standard
-
-
-
- selected_additional_filter_preview
-
- visible
-
-
- 0
- 1
- 1
- 4
- material
- standard
-
-
-
-
- current_additional_filter
- 4
- grid
-
- filter_header
-
- hidden
-
-
- 0
- 0
- 1
- 4
- material
- outline
-
-
-
- current_additional_filter_preview
-
- visible
-
-
- 0
- 1
- 1
- 4
- material
- standard
-
-
-
- merge_filters
-
- hidden
-
-
- 0
- 2
- 1
- 1
- material
- standard
-
-
-
- remove_additional_filter
-
- hidden
-
-
- 1
- 2
- 1
- 1
- material
- 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
- material
- 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
- material
- outline
-
-
-
- menu_name_as_visible
-
- visible
-
-
- 2
- 0
- 1
- 2
- material
- outline
-
-
-
- order_down
-
- editable
-
-
- 4
- 0
- 1
- 1
- 1
- material
- outline
-
-
-
- order_up
-
- editable
-
-
- 5
- 0
- 1
- 1
- 1
- material
- 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_viewDo you want to use view with tabs?
@@ -472,7 +471,6 @@
view_configuration_type
-
Pick view typemenuItemService.getAvailableViewsAsOptions(false)
@@ -626,6 +624,9 @@
PridaťAutomatické zvolenie zobrazeniaPo automatickom zvolení sa dané zobrazenie používateľovi otvorí
+ Použiť zobrazenie v taboch?
+ Vybrať zobrazenie
+ Nastavenie zobrazeniaIkonevorschau
@@ -671,6 +672,9 @@
Teile der Ziel URIAutomatische AnzeigeauswahlWenn 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_view4grid
-
View configurationuse_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 typemenuItemService.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ý filterZobrazenie prípadovVšeobecné
+ Použiť predvolené hlavičky
+ Vybrať zobrazenie
+ Nastavenie
+ Asociované zobrazenieSchaltflächentitel "Neuer Fall"
@@ -457,6 +459,10 @@
"Erweiterte Optionen" Taste bei einzelnen Fällen anzeigenMenüeintrageinstellungenFallansicht
+ Benutzerdefinierte Standardheader verwenden?
+ Wählen Sie einen Ansichtstyp
+ Einstellungen
+ zugehörige Ansicht
@@ -476,8 +482,7 @@
settings496112
-
-
+
settingsadmin
@@ -836,8 +841,7 @@
associated_view4grid
-
- Next view
+ Associated viewview_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ý filterZobrazenie úlohVšeobecné
+ Vymazať filterNeue Filter auswählen
@@ -366,6 +367,7 @@
"Erweiterte Optionen" Taste bei einzelnen Aufgaben anzeigenMenüeintrageinstellungenAufgabenansicht
+ 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_04grid
+ Generalmenu_item_identifier
@@ -755,12 +756,6 @@
outline
-
-
- general_0
- 4
- grid
- Generalmenu_name
@@ -768,7 +763,7 @@
0
- 0
+ 112
material
@@ -782,7 +777,7 @@
2
- 0
+ 111
material
@@ -796,7 +791,7 @@
3
- 0
+ 111
material
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
- gridfilter_header
@@ -571,7 +566,7 @@
0
- 0
+ 214
material
@@ -585,7 +580,7 @@
0
- 1
+ 314
material
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.netgrifapplication-engine
- 6.2.9-SNAPSHOT
+ 6.2.10jarNETGRIF Application Engine
@@ -441,8 +441,7 @@
org.springframework.session
- spring-session
- 2.0.0.M2
+ spring-session-corexmlunit
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 toolbarShould dashboard toolbar contains profile buttonfalse
+
+ 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 toolbarShould dashboard toolbar contains logout buttonfalse
+
+ 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
+
+ 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 jazykaOdhlásenie na paneli nástrojovMal 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áseniaDashboard-ID
@@ -369,6 +412,11 @@
Sollte die Symbolleiste des Dashboards ein Menü mit Sprachauswahl enthaltenAbmelden in der Dashboard-SymbolleisteDie 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
+ material
+ outline
+
+
+
+ login_url
+
+ visible
+
+
+ 2
+ 3
+ 1
+ 2
+ material
+ outline
+
+ dashboard_item_list
@@ -497,7 +573,7 @@
0
- 3
+ 422
material
@@ -511,7 +587,7 @@
2
- 3
+ 411
material
@@ -525,7 +601,7 @@
2
- 4
+ 511
material
@@ -539,7 +615,7 @@
3
- 3
+ 411
material
@@ -553,7 +629,7 @@
0
- 5
+ 612
material
@@ -567,7 +643,7 @@
2
- 5
+ 612
material
@@ -581,7 +657,7 @@
0
- 6
+ 714
material
@@ -595,7 +671,7 @@
0
- 7
+ 814
material
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_configurationtabbed_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_typePick 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
+
+
+
+ current_filter_preview
+
+
+
+ filter_header
+
+ Current filter
+
+ divider
+
+
+
+ new_filter_id
+
+
+
+ contains_filter
+
+ false
+
+
+ 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
+
+ Update view with selected filter
+
+ raised
+
+
+ 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 }
+
+
+
+ filter_case
+
+
+ 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 }
+
+
+ filter
+
+
+
+
+
+ view_header
+
+ Single task view
+
+ divider
+
+
+
+ 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
+ material
+ outline
+
+
+
+
+ view_dataGroup
+ 4
+ grid
+
+ transition_id
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 4
+ material
+ 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
+
+
+
+ current_filter_preview
+
+
+
+ filter_header
+
+ Current filter
+
+ divider
+
+
+
+ new_filter_id
+
+
+
+ contains_filter
+
+ false
+
+
+ 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
+
+ Update view with selected filter
+
+ raised
+
+
+ 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 }
+
+
+
+ filter_case
+
+
+ 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 }
+
+
+ filter
+
+
+
+
+
+ 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
+
+
+ tabbed_single_task_view_configuration
+
+
+
+ view_configuration_form
+
+
+
+
+
+
+ view_header
+
+ Ticket view
+
+ divider
+
+
+
+
+
+
+ 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
+ material
+ outline
+
+
+
+
+ associated_view
+ 4
+ grid
+ Associated view
+
+ view_configuration_type
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 4
+ 0
+ material
+ 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
+ material
+ 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_urlLogin URL
- URL address of login page
- login
+ URL address of login page. Let empty to decide by application.
@@ -389,7 +388,7 @@
URL profiluURL adresa na stránku profiluURL 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-URLURL-Adresse der ProfilseiteAnmelde-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 @@
tuneauto
-
-
-
-
-
-
-
-
-
+
+ admin
+
+ true
+ true
+ true
+ true
+
+ configuration_d14
@@ -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 @@
settingsauto
-
-
-
-
-
-
-
-
-
+
+ admin
+
+ true
+ true
+ true
+ true
+
+ configuration_d14
@@ -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ť zobrazenieNastavenieAsociované zobrazenie
-
-
-
+ Zobrazenie jednej úlohy
+ ID prechoduNeue Filter auswählen
@@ -207,9 +205,8 @@
Wählen Sie einen AnsichtstypEinstellungenzugehö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ť zobrazenieNastavenieAsociované zobrazenie
-
-
+ Tiketové zobrazenieNeue Filter auswählen
@@ -252,8 +251,7 @@
Wählen Sie einen AnsichtstypEinstellungenzugehö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_typePick 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.netgrifapplication-engine
- 6.2.9-SNAPSHOT
+ 6.2.10jarNETGRIF 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 itemsautocomplete_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
@@ -357,8 +357,8 @@
ID položky paneluUnikátny identifikátor položky panelu
- Existujúce menu položky
- Zoznam všetkých existujúcich menu položiek
+ Existujúce menu položky
+ Zoznam všetkých existujúcich menu položiekInterná položkaPoložka interného menu alebo odkaz na externú webovú adresu URLExterná adresa URL
@@ -384,8 +384,8 @@
Dashboard-Element-IDEindeutiger Bezeichner des Dashboard-Elements
- Vorhandene Menüpunkte
- Liste aller vorhandenen Menüpunkte
+ Vorhandene Menüpunkte
+ Liste aller vorhandenen MenüpunkteInterner PostenInterner Menüpunkt oder Link zu externer Website-URLExterne URL
@@ -464,7 +464,7 @@
4grid
- preference_items_list
+ menu_items_listeditable
@@ -623,7 +623,7 @@
- preference_item_taskRef
+ menu_item_taskRefhidden
@@ -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 @@
- dashboard_item_to_preference_item
+ dashboard_item_to_menu_item
@@ -89,7 +89,7 @@
order_down_set
- 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()) }
@@ -127,7 +127,7 @@
order_up_set
- 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()) }
@@ -203,7 +203,7 @@
add_new_item_set
- 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 @@
remove_new_item_set
- 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
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
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 createImportFiltersNet() {
helper.importProcess("Petri net for importing filters", IMPORT_NET_IDENTIFIER, IMPORT_FILTER_FILE_NAME)
+ }
+
+ Optional 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
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 options, @Nullable Object fieldValue){
+ Map 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> 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/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
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 dashboardItems;
+ /**
+ * mapping for menuItems to DashboardItems
+ */
+ private HashMap 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 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 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> 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 @@
+
+
+ menu_item_delete
+
+
+ removeRefFromParent()
+
+
+
+
+
systemSystem
@@ -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_idDashboard 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_importMEI
- Menu import
+ Dashboard importdevice_hubtruetrue
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.netgrifquartz-mongodb-connector
- 1.0.0-SNAPSHOT
+ 1.0.0
@@ -860,14 +860,14 @@
ossrh-publish
- ossrh
+ centralCentral Repository OSSRH
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+ https://central.sonatype.com/
- ossrh
+ centralCentral 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
+
+
+ action_api_improvements
+
+
+
+ enumeration
+
+
+
+ enumeration_map
+
+
+
+ multichoice
+
+
+
+ multichoice_map
+
+
+
+ stringCollection
+
+
+
+ taskRef
+
+
+
+ text
+
+
+
+ t1
+ 336
+ 112
+
+
+ t1_0
+ 4
+ grid
+
+ enumeration_map
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ caseRef
+
+ editable
+
+
+ 2
+ 0
+ 1
+ 2
+ material
+ outline
+
+
+
+ taskRef
+
+ editable
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ outline
+
+
+
+ stringCollection
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 2
+ material
+ outline
+
+
+
+ enumeration
+
+ editable
+
+
+ 2
+ 2
+ 1
+ 2
+ material
+ outline
+
+
+
+ multichoice
+
+ editable
+
+
+ 0
+ 3
+ 1
+ 2
+ material
+ outline
+
+
+
+ text
+
+ editable
+
+
+ 2
+ 3
+ 1
+ 2
+ material
+ outline
+
+
+
+ multichoice_map
+
+ editable
+
+
+ 2
+ 4
+ 1
+ 2
+ material
+ outline
+
+
+
+
+
+ t2
+ 208
+ 240
+
+
+
+ t3
+ 336
+ 240
+
+
+
+ t4
+ 464
+ 240
+
+
+
+ p1
+ 208
+ 112
+ 1
+ false
+
+
+ p2
+ 464
+ 112
+ 0
+ false
+
+
+ a1
+ regular
+ p1
+ t1
+ 1
+
+
+ a2
+ regular
+ t1
+ p2
+ 1
+
+
\ No newline at end of file
From a1986acaea8be783ee191ce1aeac91cadab9d081 Mon Sep 17 00:00:00 2001
From: palajsamuel
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)
} 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 findCases(List 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.
*
- *
If the field value is {@code null}, this method returns an empty list.
+ *
If the field value is {@code null}, this method returns {@code null}.
*
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
@@ -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.
*
- *
If the field value is {@code null}, this method returns an empty list.
+ *
If the field value is {@code null}, this method returns {@code null}.
*
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
@@ -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 finishTasks(List tasks, IUser finisher = userService.loggedOrSystem, Map params = [:]) {
- List outcomes = taskService.finishTasks(tasks, finisher)
+ List 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 findTasks(List 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 findPetriNets(List 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 findPetriNetsByObjectIds(List 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 transitionIds = ["t1", "t2"]
IUser user = userService.getLoggedOrSystem()
-// testing "byTransition" variants should be enough, as they call methods, that tak List instead of List
+// testing "byTransition" variants should be enough, as they call methods, that take List instead of List
List 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)[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 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)[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 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 @@
action_api_improvements1.0.0NEW
- New Model
+ Action API Improvements Testdevice_hubtruetrue
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.
+ * 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.
+ *
+ */
+public class MenuItemTemplateHolder {
+ /**
+ * Map of available menu item templates indexed by their unique identifiers.
+ *
+ * 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.
+ *
+ */
+ public static Map 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.
+ *
+ * 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.
+ *
+ *
+ * @return a map where keys are template identifiers and values are internationalized template names
+ */
+ public static Map 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 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 @@
+
+ configuration_templates
+ Pick a configuration template
+
+ com.netgrif.application.engine.menu.service.MenuItemTemplateHolder.transformToOptions()
+
+
@@ -812,6 +819,20 @@
outline
+
+ configuration_templates
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 4
+ material
+ 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 templateOpt = MenuItemTemplateHolder.get(selectedTemplate);
+ if (templateOpt.isEmpty()) {
+ throw new IllegalArgumentException(String.format("No configuration template found with name: %s", selectedTemplate));
+ }
+
+ MenuItemBody menuItemBody = templateOpt.get().getTemplate();
+ Case viewCase = createView(menuItemBody.getView(), menuItemBody.isUseTabbedView());
+ ToDataSetOutcome dataSetOutcome = menuItemBody.toDataSetByConfigTemplate(viewCase);
+ log.debug("For menu item: [{}. {}] was used configuration template: {}", menuItemCase.getStringId(),
+ menuItemIdentifier, selectedTemplate);
+ return new ConfigurationTemplateOutcome(dataSetOutcome);
}
protected Case findCase(String processIdentifier, String query) {
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
index 10070526211..29232fbb82a 100644
--- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
+++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
@@ -4,6 +4,7 @@
import com.netgrif.application.engine.petrinet.domain.I18nString;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -23,7 +24,7 @@ public class MenuItemTemplateHolder {
* The key is the template identifier (String), and the value is the Template instance.
*
*/
- public static Map templates = Map.of(
+ private final static Map templates = Map.of(
TabbedCaseViewTemplate.IDENTIFIER, new TabbedCaseViewTemplate(),
TabbedTaskViewTemplate.IDENTIFIER, new TabbedTaskViewTemplate(),
SimpleCaseViewTemplate.IDENTIFIER, new SimpleCaseViewTemplate(),
@@ -32,6 +33,21 @@ SingleTaskViewTemplate.IDENTIFIER, new SingleTaskViewTemplate(),
TabbedTicketViewTemplate.IDENTIFIER, new TabbedTicketViewTemplate()
);
+
+ /**
+ * Retrieves a template by its unique identifier.
+ *
+ * This method looks up and returns the Template instance associated with the
+ * provided identifier from the templates registry.
+ *
+ *
+ * @param identifier the unique identifier of the template to retrieve
+ * @return an Optional containing the Template instance if found, or empty Optional if no template exists for the given identifier
+ */
+ public static Optional get(String identifier) {
+ return Optional.ofNullable(templates.get(identifier));
+ }
+
/**
* Transforms the available templates into a map of selectable options.
*
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 5f0ef140ddf..bf3404cb10b 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
@@ -1,5 +1,6 @@
package com.netgrif.application.engine.menu.service.interfaces;
+import com.netgrif.application.engine.menu.domain.ConfigurationTemplateOutcome;
import com.netgrif.application.engine.menu.domain.FilterBody;
import com.netgrif.application.engine.menu.domain.MenuItemBody;
import com.netgrif.application.engine.menu.domain.MenuItemView;
@@ -31,7 +32,7 @@ public interface IMenuItemService {
Case duplicateItem(Case originItem, I18nString newTitle, String newIdentifier) throws TransitionNotExecutableException;
Case removeChildItemFromParent(String folderId, Case childItem);
List getMenuItemData(String caseId, Locale locale);
- void handleConfigurationTemplate(Case menuItemCase);
+ ConfigurationTemplateOutcome handleConfigurationTemplate(Case menuItemCase) throws TransitionNotExecutableException;
/**
* 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 01ebb9eae98..deb25683429 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -849,13 +849,10 @@
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
+ def outcome = menuItemService.handleConfigurationTemplate(useCase)
+ outcome.mapping.each { fieldId, value ->
+ change useCase.getField(fieldId) value { value }
}
-
- menuItemService.handleConfigurationTemplate(configuration_templates.value)
Create
From a8f8645be5e4172deddc15919ee040c1a11aaa84 Mon Sep 17 00:00:00 2001
From: chvostek
Date: Mon, 18 May 2026 11:54:54 +0200
Subject: [PATCH 081/174] [ETASK-23] Dynamic view configuration - resolve todos
- fix behavior in menu_item.xml
---
.../menu/domain/ConfigurationTemplateOutcome.java | 4 +++-
.../domain/templates/SimpleCaseViewTemplate.java | 2 +-
.../domain/templates/SimpleTaskViewTemplate.java | 2 +-
.../domain/templates/SingleTaskViewTemplate.java | 2 +-
.../domain/templates/TabbedTicketViewTemplate.java | 2 +-
.../engine/menu/web/MenuController.java | 4 ++--
.../petriNets/engine-processes/menu/menu_item.xml | 14 +++++++++++---
7 files changed, 20 insertions(+), 10 deletions(-)
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
index e31d6bbdbea..d85be58223f 100644
--- a/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java
+++ b/src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java
@@ -4,7 +4,9 @@
import java.util.Map;
public class ConfigurationTemplateOutcome {
- // todo 23 doc
+ /**
+ * Map of field data where the key is field ID and the value is field value.
+ */
public final Map mapping;
public ConfigurationTemplateOutcome() {
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 a23b243f7eb..1cfc62302df 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
@@ -11,7 +11,7 @@ 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
+ Map.of("sk", "Zobrazenie prípadov", "de", "Fallansicht"));
private static final MenuItemBody TEMPLATE = buildTemplate();
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 aa4815c2f6e..36f1c77a948 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
@@ -11,7 +11,7 @@ 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
+ Map.of("sk", "Zobrazenie úloh", "de", "Aufgabenansicht"));
private static final MenuItemBody TEMPLATE = buildTemplate();
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 98ea5f60190..835dd50da3b 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
@@ -11,7 +11,7 @@ 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
+ Map.of("sk", "Zobrazenie jednej úlohy", "de", "Anzeige einer Aufgabe"));
private static final MenuItemBody TEMPLATE = buildTemplate();
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 347b0d96cdb..8eb59a20568 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
@@ -12,7 +12,7 @@ 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
+ Map.of("sk", "Tiketové zobrazenie", "de", "Ticketansicht"));
private static final MenuItemBody TEMPLATE = buildTemplate();
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 32e32f3fdde..99ffa224aaa 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
@@ -30,7 +30,7 @@ public class MenuController {
private final IMenuItemService menuItemService;
- // todo 23 menu item authorization
+ // todo 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 getMenuItemData(@PathVariable("encodedCaseId") String encodedCaseId, Locale locale) {
@@ -44,6 +44,6 @@ public EntityModel getMenuItemData(@PathVariable("encodedC
}
}
- // todo 23 search with authorization
+ // todo search with authorization
}
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 deb25683429..666f7cba23f 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -449,6 +449,17 @@
use_tabbed_viewDo you want to use view with tabs?true
+
+ 0
+
+
+ use_tabbed_view: f.use_tabbed_view,
+ use_tab_icon: f.use_tab_icon;
+
+ manageBehaviorOfTabFields(use_tab_icon.value, use_tabbed_view.value)
+
+
+
@@ -1550,11 +1561,8 @@
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, true) }
From fb86d8f4f8c1036961df7f562c78bebffbe0a94d Mon Sep 17 00:00:00 2001
From: chvostek
Date: Mon, 18 May 2026 15:49:41 +0200
Subject: [PATCH 082/174] [ETASK-23] Dynamic view configuration - add menu item
body validation - add tags in menu_item.xml transitions for better filtering
---
.../engine/menu/service/MenuItemService.java | 18 ++++++++++++++++--
.../engine-processes/menu/menu_item.xml | 16 +++++++++++++++-
2 files changed, 31 insertions(+), 3 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 f7626296cfd..6ef36992247 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
@@ -95,6 +95,8 @@ public Case updateFilter(Case filterCase, FilterBody body) {
* */
@Override
public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableException {
+ validateMenuItemBody(body);
+
log.debug("Creation of menu item case with identifier [{}] started.", body.getIdentifier());
IUser loggedUser = userService.getLoggedOrSystem();
String sanitizedIdentifier = MenuItemUtils.sanitize(body.getIdentifier());
@@ -103,8 +105,6 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce
throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", sanitizedIdentifier));
}
- // todo 23 validation
-
Case parentItemCase = getOrCreateFolderItem(body.getUri());
I18nString newName = body.getMenuName();
if (newName == null) {
@@ -140,6 +140,8 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce
* */
@Override
public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNotExecutableException {
+ validateMenuItemBody(body);
+
log.debug("Update of menu item case with identifier [{}] started.", body.getIdentifier());
String actualUriNodeId = uriService.findByUri(body.getUri()).getStringId();
if (!itemCase.getUriNodeId().equals(actualUriNodeId)) {
@@ -451,6 +453,18 @@ public ConfigurationTemplateOutcome handleConfigurationTemplate(Case menuItemCas
return new ConfigurationTemplateOutcome(dataSetOutcome);
}
+ protected void validateMenuItemBody(MenuItemBody menuItemBody) {
+ if (menuItemBody == null) {
+ throw new IllegalArgumentException("Input data cannot be null");
+ }
+ if (menuItemBody.getIdentifier() == null) {
+ throw new IllegalArgumentException("Identifier cannot be null");
+ }
+ if (menuItemBody.getUri() == null || menuItemBody.getUri().isBlank()) {
+ throw new IllegalArgumentException("Uri cannot be null");
+ }
+ }
+
protected Case findCase(String processIdentifier, String query) {
CaseSearchRequest request = CaseSearchRequest.builder()
.process(Collections.singletonList(new CaseSearchRequest.PetriNet(processIdentifier)))
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 666f7cba23f..49236ce8d14 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -759,7 +759,9 @@
340220
-
+
+ true
+ hourglass_emptyauto
@@ -887,6 +889,9 @@
460100
+
+ true
+ settingsauto
@@ -1019,6 +1024,9 @@
580100
+
+ true
+ move_downauto
@@ -1100,6 +1108,9 @@
580340
+
+ true
+ content_copyauto
@@ -1162,6 +1173,9 @@
580220
+
+ true
+ low_priorityauto
From 460835a8b260145682ff2666e96ea83111535ded Mon Sep 17 00:00:00 2001
From: chvostek
Date: Tue, 19 May 2026 16:11:14 +0200
Subject: [PATCH 083/174] [ETASK-23] Dynamic view configuration - change
default headers field type to string collection - introduce
CustomViewTemplate - update default filter bodies to exclude allowed nets
---
.../engine/menu/domain/FilterBody.java | 12 +-
.../domain/configurations/CaseViewBody.java | 6 +-
.../domain/configurations/TaskViewBody.java | 6 +-
.../domain/templates/CustomViewTemplate.java | 37 ++++
.../menu/domain/templates/Template.java | 2 -
.../menu/service/MenuItemTemplateHolder.java | 3 +-
.../engine/workflow/service/DataService.java | 6 +-
.../menu/case_view_configuration.xml | 94 ++++++--
.../engine-processes/menu/menu_item.xml | 202 +++++++-----------
.../menu/task_view_configuration.xml | 16 +-
10 files changed, 211 insertions(+), 173 deletions(-)
create mode 100644 src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
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 de048064e33..8735b9fcf1b 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
@@ -62,12 +62,12 @@ public ToDataSetOutcome toDataSet() {
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
- ));
+ Map dataSetValues = new HashMap<>();
+ dataSetValues.put("type", "filter");
+ dataSetValues.put("value", this.query);
+ dataSetValues.put("filterMetadata", metadata);
+ dataSetValues.put("allowedNets", this.allowedNets);
+ outcome.getDataSet().put(DefaultFiltersRunner.FILTER_FIELD_ID, dataSetValues);
return outcome;
}
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 7be6230497a..2e7edb283c4 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
@@ -68,8 +68,10 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) {
this.headersMode == null ? new ArrayList<>() : this.headersMode);
outcome.putDataSetEntry(CaseViewConstants.FIELD_HEADERS_DEFAULT_MODE, FieldType.ENUMERATION_MAP,
this.headersDefaultMode);
- outcome.putDataSetEntry(CaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT,
- this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null);
+ if (this.defaultHeaders != null) {
+ outcome.putDataSetEntry(CaseViewConstants.FIELD_DEFAULT_HEADERS, FieldType.STRING_COLLECTION,
+ this.defaultHeaders);
+ }
outcome.putDataSetEntry(CaseViewConstants.FIELD_IS_HEADER_MODE_CHANGEABLE, FieldType.BOOLEAN,
this.isHeaderModeChangeable);
outcome.putDataSetEntry(CaseViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN,
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 7ad90df3469..c0e40cc6b92 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
@@ -56,8 +56,10 @@ protected ToDataSetOutcome toDataSetInternal(ToDataSetOutcome outcome) {
this.allowHeaderTableMode);
outcome.putDataSetEntry(TaskViewConstants.FIELD_USE_DEFAULT_HEADERS, FieldType.BOOLEAN,
this.useDefaultHeaders);
- outcome.putDataSetEntry(TaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.TEXT,
- this.defaultHeaders != null ? String.join(",", this.defaultHeaders) : null);
+ if (this.defaultHeaders != null) {
+ outcome.putDataSetEntry(TaskViewConstants.FIELD_DEFAULT_HEADERS, FieldType.STRING_COLLECTION,
+ this.defaultHeaders);
+ }
outcome.putDataSetEntry(TaskViewConstants.FIELD_SHOW_MORE_MENU, FieldType.BOOLEAN,
this.showMoreMenu);
if (this.emptyContentText != null) {
diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
new file mode 100644
index 00000000000..95d2e7f6b45
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
@@ -0,0 +1,37 @@
+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 CustomViewTemplate implements Template {
+ public static final String IDENTIFIER = "none";
+
+ private static final I18nString NAME = new I18nString("Custom view",
+ Map.of("sk", "Vlastné zobrazenie", "de", "Benutzerdefinierte Ansicht"));
+
+ private static final MenuItemBody TEMPLATE = buildTemplate();
+
+ private static MenuItemBody buildTemplate() {
+ MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setUseTabbedView(false);
+ menuItemBody.setUseCustomView(true);
+
+ 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
index c6c2a462842..f4e61b80a87 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
@@ -18,7 +18,6 @@ static FilterBody defaultTaskFilterBody(I18nString name) {
filterBody.setVisibility("private");
filterBody.setTitle(name);
filterBody.setQuery("*");
- filterBody.setAllowedNets(List.of());
return filterBody;
}
@@ -29,7 +28,6 @@ static FilterBody defaultCaseFilterBody(I18nString name) {
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/MenuItemTemplateHolder.java b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
index 29232fbb82a..ef862551927 100644
--- a/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
+++ b/src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
@@ -30,7 +30,8 @@ TabbedTaskViewTemplate.IDENTIFIER, new TabbedTaskViewTemplate(),
SimpleCaseViewTemplate.IDENTIFIER, new SimpleCaseViewTemplate(),
SimpleTaskViewTemplate.IDENTIFIER, new SimpleTaskViewTemplate(),
SingleTaskViewTemplate.IDENTIFIER, new SingleTaskViewTemplate(),
- TabbedTicketViewTemplate.IDENTIFIER, new TabbedTicketViewTemplate()
+ TabbedTicketViewTemplate.IDENTIFIER, new TabbedTicketViewTemplate(),
+ CustomViewTemplate.IDENTIFIER, new CustomViewTemplate()
);
diff --git a/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java b/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java
index 70a6903f40a..11c7271f6a0 100644
--- a/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java
+++ b/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java
@@ -6,6 +6,7 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.netgrif.application.engine.auth.domain.IUser;
import com.netgrif.application.engine.auth.service.interfaces.IUserService;
@@ -981,10 +982,11 @@ private List parseListStringAllowedNets(ObjectNode node) {
}
private List parseListString(ObjectNode node, String attributeKey) {
- ArrayNode arrayNode = (ArrayNode) node.get(attributeKey);
- if (arrayNode == null) {
+ Object objectNode = node.get(attributeKey);
+ if (objectNode == null || objectNode instanceof NullNode) {
return null;
}
+ ArrayNode arrayNode = (ArrayNode) objectNode;
ArrayList list = new ArrayList<>();
arrayNode.forEach(string -> list.add(string.asText()));
return list;
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 b93951a6913..3ab0ef11da9 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
@@ -394,22 +394,14 @@
Use custom default headers?true
-
+
case_default_headersSet default headers
- Example: "meta-title,meta-visualId"
-
- defaultHeaders: f.this;
-
- String trimmed = defaultHeaders.value?.replaceAll("\\s","")
- if (defaultHeaders.value != trimmed) {
- change defaultHeaders value { trimmed }
- }
-
+ Example: "meta-title", "meta-visualId"view_configuration_type
- Pick view type
+ View typemenuItemService.getAvailableViewsAsOptions(true, useCase.processIdentifier)
@@ -448,7 +440,7 @@
advanced_content_form: f.advanced_content_form;
- String nextViewTaskId = useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}?.task
+ String nextViewTaskId = useCase.tasks.find { taskPair -> taskPair.transition == "open_case_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]
@@ -506,7 +498,7 @@
Identifikátor ikony tlačidla "Nová inštancia"Náhľad ikonyPredvolené hlavičky
- Napríklad: "meta-title,meta-visualId"
+ Napríklad: "meta-title", "meta-visualId"Zvoľte nový filterAktualizovať zobrazenie s vybraným filtromTyp vyhľadávania prípadov
@@ -530,7 +522,7 @@
Súčasný filterZobrazenie prípadovVšeobecné
- Vybrať zobrazenie
+ Typ zobrazeniaNastavenie
@@ -539,7 +531,7 @@
IkonevorschauAnzuzeigende Attributmenge auswählenNeue Filter auswählen
- Beispiel: "meta-title,meta-visualId"
+ Beispiel: "meta-title", "meta-visualId"VerstecktEinfacher SuchmodusSortieren
@@ -562,7 +554,7 @@
"Erweiterte Optionen" Taste bei einzelnen Fällen anzeigenMenüeintrageinstellungenFallansicht
- Wählen Sie einen Ansichtstyp
+ Der AnsichtstypEinstellungen
@@ -778,10 +770,10 @@
- next_view_settings
+ open_case_settings33648
-
+
queue_play_nextauto
@@ -1325,6 +1317,32 @@
+
+ change_to_tabbed
+ 240
+ 144
+
+
+ system
+
+ true
+
+
+
+
+
+
+ change_to_untabbed
+ 112
+ 48
+
+
+ system
+
+ true
+
+
+ initialized496
@@ -1349,6 +1367,14 @@
0false
+
+ is_untabbed
+ 112
+ 144
+
+ 0
+ false
+ a1read
@@ -1381,7 +1407,7 @@
a5readinitialized
- next_view_settings
+ open_case_settings1
@@ -1410,7 +1436,35 @@
a9readis_tabbed
- next_view_settings
+ open_case_settings
+ 1
+
+
+ a10
+ regular
+ is_tabbed
+ change_to_untabbed
+ 1
+
+
+ a11
+ regular
+ change_to_untabbed
+ is_untabbed
+ 1
+
+
+ a12
+ regular
+ change_to_tabbed
+ is_tabbed
+ 1
+
+
+ a14
+ regular
+ is_untabbed
+ change_to_tabbed1
\ 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 49236ce8d14..5183f6289ab 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -290,7 +290,7 @@
menu_name_as_visibleName of the item
- Is shown in the menu
+ Will be shown in the menuautocomplete
@@ -503,7 +503,7 @@
view_configuration_type
- Pick view type
+ View typemenuItemService.getAvailableViewsAsOptions(true, true)
@@ -643,7 +643,7 @@
configuration_templates
- Pick a configuration template
+ Configuration templatecom.netgrif.application.engine.menu.service.MenuItemTemplateHolder.transformToOptions()
@@ -695,7 +695,7 @@
Automatické zvolenie zobrazeniaPo automatickom zvolení sa dané zobrazenie používateľovi otvoríPoužiť zobrazenie v taboch?
- Vybrať zobrazenie
+ Typ zobrazeniaNastavenie zobrazeniaVytvoriť menu položkuZoradiť pod-položky
@@ -746,7 +746,7 @@
Automatische AnzeigeauswahlWenn ausgewählt, wird die Ansicht automatisch geöffnetMöchten Sie die Ansicht mit Registerkarten verwenden?
- Wählen Sie einen Ansichtstyp
+ Der AnsichtstypDie AnsichtskonfigurationMenüeintrag erstellenUntereintrage sortieren
@@ -775,7 +775,7 @@
4grid
- menu_item_identifier
+ menu_nameeditablerequired
@@ -784,7 +784,44 @@
001
- 4
+ 2
+ material
+ outline
+
+
+ 0
+
+
+ menu_item_identifier: f.menu_item_identifier,
+ menu_name: f.menu_name;
+
+ if (menu_item_identifier.value != null && !menu_item_identifier.value.isEmpty()
+ || menu_name.value == null) {
+ return
+ }
+
+ String defaultName = menu_name.value.defaultValue
+ if (defaultName == null || defaultName.isEmpty()) {
+ defaultName = menu_name.value.translations?.values()?.find()
+ }
+
+ String identifier = com.netgrif.application.engine.menu.utils.MenuItemUtils.sanitize(defaultName)
+ change menu_item_identifier value { identifier }
+
+
+
+
+
+ menu_item_identifier
+
+ editable
+ required
+
+
+ 2
+ 0
+ 1
+ 2
material
outline
@@ -836,6 +873,7 @@
configuration_templateseditable
+ required0
@@ -859,7 +897,6 @@
newUri = newUri.replace("//","/")
changeMenuItem useCase uri { newUri }
- change name value { new com.netgrif.application.engine.petrinet.domain.I18nString(identifier.value) }
def outcome = menuItemService.handleConfigurationTemplate(useCase)
@@ -939,9 +976,9 @@
- menu_name
+ configuration_templates
- editable
+ visible0
@@ -953,7 +990,7 @@
- menu_icon
+ menu_nameeditable
@@ -966,6 +1003,20 @@
outline
+
+ menu_icon
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 2
+ material
+ outline
+
+ menu_icon_preview
@@ -987,7 +1038,7 @@
0
- 2
+ 312
material
@@ -1001,7 +1052,7 @@
0
- 3
+ 414
material
@@ -1502,9 +1553,9 @@
3grid
- use_custom_view
+ view_configuration_type
- editable
+ visible0
@@ -1514,39 +1565,11 @@
material
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
+ tab_name
- hidden
+ editable1
@@ -1558,30 +1581,18 @@
- use_tabbed_view
+ custom_view_selector
- editable
+ hidden
- 1
+ 001
- 1
+ 3
material
- standard
+ outline
-
- 0
-
-
- use_tabbed_view: f.use_tabbed_view,
- view_configuration_type: f.view_configuration_type;
-
- change view_configuration_type value { null }
- change view_configuration_type options { menuItemService.getAvailableViewsAsOptions(use_tabbed_view.value, true) }
-
-
- use_tab_icon
@@ -1589,8 +1600,8 @@
editable
- 2
- 0
+ 0
+ 1110
@@ -1608,20 +1619,6 @@
-
- tab_name
-
- editable
-
-
- 0
- 1
- 1
- 1
- material
- outline
-
- tab_icon
@@ -1650,53 +1647,6 @@
standard
-
- view_configuration_type
-
- editable
-
-
- 0
- 2
- 1
- 3
- material
- outline
-
-
- 0
-
-
- 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,
- 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 { [] }
- change view_configuration_all_data_form value { [] }
- return
- }
-
- 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)
- 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] }
-
-
-
- view_configuration_form
@@ -1706,7 +1656,7 @@
031
- 3
+ 2
material
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 b8298bfa552..5ffa5aa3732 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
@@ -287,18 +287,10 @@
Use custom default headers?true
-
+
task_default_headersSet default headers
- Example: "meta-title,meta-user"
-
- defaultHeaders: f.this;
-
- String trimmed = defaultHeaders.value?.replaceAll("\\s","")
- if (defaultHeaders.value != trimmed) {
- change defaultHeaders value { trimmed }
- }
-
+ Example: "meta-title", "meta-user"task_show_more_menu
@@ -377,7 +369,7 @@
Povoliť tabuľkový mód pre hlavičky?Použiť vlastné predvolené hlavičky?Predvolené hlavičky
- Napríklad: "meta-title,meta-user"
+ Napríklad: "meta-title", "meta-user"Zobrazovať menu pre úlohovú položku?Nastavenie položkySúčasný filter
@@ -397,7 +389,7 @@
Erlaube Tabellenmodus?Eigene Kopfzeilen verwenden?Anzuzeigende Attributmenge auswählen
- Beispiel: "meta-title,meta-user"
+ Beispiel: "meta-title", "meta-user"Aktueller FilterAllgemeinAktualisiere die Ansicht mit dem ausgewählten Filter
From 150a3afbab0e2118e53a7956e44460063c57acd5 Mon Sep 17 00:00:00 2001
From: chvostek
Date: Wed, 20 May 2026 08:55:46 +0200
Subject: [PATCH 084/174] [ETASK-23] Dynamic view configuration - update
CustomViewTemplate - introduce allAllowedNets metadata for filter - fix
behavior in menu_item.xml
---
.../engine/menu/domain/FilterBody.java | 5 ++-
.../domain/templates/CustomViewTemplate.java | 4 +-
.../engine-processes/menu/menu_item.xml | 39 +++++++++++++++++++
3 files changed, 43 insertions(+), 5 deletions(-)
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 8735b9fcf1b..cd96a400206 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
@@ -35,7 +35,7 @@ public FilterBody(Case filterCase) {
*
* @return metadata containing filter type as map
* */
- public static Map getDefaultMetadata(String type) {
+ public static Map getDefaultMetadata(String type, boolean allAllowedNets) {
Map resultMap = new HashMap<>();
resultMap.put("searchCategories", List.of());
@@ -43,6 +43,7 @@ public static Map getDefaultMetadata(String type) {
resultMap.put("filterType", type);
resultMap.put("defaultSearchCategories", true);
resultMap.put("inheritAllowedNets", false);
+ resultMap.put("allAllowedNets", allAllowedNets);
return resultMap;
}
@@ -60,7 +61,7 @@ public ToDataSetOutcome toDataSet() {
outcome.putDataSetEntry(DefaultFiltersRunner.FILTER_I18N_TITLE_FIELD_ID, FieldType.I18N, this.title);
Map metadata = this.metadata;
if (metadata == null) {
- metadata = getDefaultMetadata(this.type);
+ metadata = getDefaultMetadata(this.type, this.allowedNets == null);
}
Map dataSetValues = new HashMap<>();
dataSetValues.put("type", "filter");
diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
index 95d2e7f6b45..538198a993f 100644
--- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
+++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
@@ -1,14 +1,12 @@
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 CustomViewTemplate implements Template {
- public static final String IDENTIFIER = "none";
+ public static final String IDENTIFIER = "custom_view";
private static final I18nString NAME = new I18nString("Custom view",
Map.of("sk", "Vlastné zobrazenie", "de", "Benutzerdefinierte Ansicht"));
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 5183f6289ab..1044e970802 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -898,6 +898,23 @@
changeMenuItem useCase uri { newUri }
+
+ trans: t.view_settings,
+ useTabIcon: f.use_tab_icon,
+ 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_custom_view: f.use_custom_view,
+ selector: f.custom_view_selector;
+
+ if (use_custom_view.value) {
+ make selector, editable on trans when { true }
+ make [useTabIcon, tabIconPreview, tabName, tabIcon, view_configuration_form,
+ view_configuration_type], hidden on trans when { true }
+ }
+
def outcome = menuItemService.handleConfigurationTemplate(useCase)
outcome.mapping.each { fieldId, value ->
@@ -1357,6 +1374,28 @@
true
+
+ finish
+
+
+ trans: t.view_settings,
+ useTabIcon: f.use_tab_icon,
+ 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_custom_view: f.use_custom_view,
+ selector: f.custom_view_selector;
+
+ if (use_custom_view.value) {
+ make selector, editable on trans when { true }
+ make [useTabIcon, tabIconPreview, tabName, tabIcon, view_configuration_form,
+ view_configuration_type], hidden on trans when { true }
+ }
+
+
+ role_settings
From 0081969b3df4c19533f1fef7dd5637cec5dd8e61 Mon Sep 17 00:00:00 2001
From: chvostek
Date: Wed, 20 May 2026 10:39:53 +0200
Subject: [PATCH 085/174] [ETASK-23] Dynamic view configuration - update
templates configuration - fix custom view creation via template - rework
settings parts UX
---
.../engine/menu/domain/MenuItemBody.java | 3 +
.../domain/templates/CustomViewTemplate.java | 1 +
.../templates/SimpleCaseViewTemplate.java | 1 +
.../templates/SimpleTaskViewTemplate.java | 1 +
.../templates/SingleTaskViewTemplate.java | 1 +
.../templates/TabbedCaseViewTemplate.java | 1 +
.../templates/TabbedTaskViewTemplate.java | 1 +
.../templates/TabbedTicketViewTemplate.java | 1 +
.../engine/menu/service/MenuItemService.java | 5 +-
.../menu/case_view_configuration.xml | 69 ++++++-------------
.../engine-processes/menu/menu_item.xml | 47 +++++++++----
.../menu/single_task_view_configuration.xml | 23 ++++---
.../menu/tabbed_ticket_view_configuration.xml | 61 +++++-----------
.../menu/task_view_configuration.xml | 25 ++++---
14 files changed, 116 insertions(+), 124 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 bb19500b411..51787157310 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
@@ -23,6 +23,7 @@
public class MenuItemBody {
private String uri;
private String identifier;
+ private String configurationTemplateIdentifier;
private String menuIcon = "filter_none";
private I18nString menuName;
@@ -182,6 +183,7 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas
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);
+ outcome.putDataSetEntry(MenuItemConstants.FIELD_CONFIGURATION_TEMPLATES, FieldType.ENUMERATION_MAP, this.configurationTemplateIdentifier);
outcome = toDataSetWithView(viewCase, outcome);
@@ -197,6 +199,7 @@ public ToDataSetOutcome toDataSet(String parentId, String nodePath, Case viewCas
public ToDataSetOutcome toDataSetByConfigTemplate(Case viewCase) {
ToDataSetOutcome outcome = new ToDataSetOutcome();
outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_TABBED_VIEW, FieldType.BOOLEAN, this.useTabbedView);
+ outcome.putDataSetEntry(MenuItemConstants.FIELD_USE_CUSTOM_VIEW, FieldType.BOOLEAN, this.useCustomView);
return toDataSetWithView(viewCase, outcome);
}
diff --git a/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java b/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
index 538198a993f..213d3f829ca 100644
--- a/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
+++ b/src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
@@ -15,6 +15,7 @@ public class CustomViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(false);
menuItemBody.setUseCustomView(true);
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 1cfc62302df..1bb984dba48 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
@@ -17,6 +17,7 @@ public class SimpleCaseViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(false);
CaseViewBody caseViewBody = new CaseViewBody();
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 36f1c77a948..b7bdf81bfb0 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
@@ -17,6 +17,7 @@ public class SimpleTaskViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(false);
TaskViewBody taskViewBody = new TaskViewBody();
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 835dd50da3b..648a185b7ac 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
@@ -17,6 +17,7 @@ public class SingleTaskViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(false);
SingleTaskViewBody singleTaskViewBody = new SingleTaskViewBody();
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 609384ad046..36da1030d11 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
@@ -18,6 +18,7 @@ public class TabbedCaseViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(true);
CaseViewBody caseViewBody = new CaseViewBody();
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 47877bf8c1c..60eb0733c7d 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
@@ -17,6 +17,7 @@ public class TabbedTaskViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(true);
TaskViewBody taskViewBody = new TaskViewBody();
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 8eb59a20568..84af0717833 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,6 +18,7 @@ public class TabbedTicketViewTemplate implements Template {
private static MenuItemBody buildTemplate() {
MenuItemBody menuItemBody = new MenuItemBody();
+ menuItemBody.setConfigurationTemplateIdentifier(IDENTIFIER);
menuItemBody.setUseTabbedView(true);
TabbedTicketViewBody tabbedTicketViewBody = new TabbedTicketViewBody();
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 6ef36992247..fdcb7c86717 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
@@ -446,7 +446,10 @@ public ConfigurationTemplateOutcome handleConfigurationTemplate(Case menuItemCas
}
MenuItemBody menuItemBody = templateOpt.get().getTemplate();
- Case viewCase = createView(menuItemBody.getView(), menuItemBody.isUseTabbedView());
+ Case viewCase = null;
+ if (menuItemBody.hasView()) {
+ viewCase = createView(menuItemBody.getView(), menuItemBody.isUseTabbedView());
+ }
ToDataSetOutcome dataSetOutcome = menuItemBody.toDataSetByConfigTemplate(viewCase);
log.debug("For menu item: [{}. {}] was used configuration template: {}", menuItemCase.getStringId(),
menuItemIdentifier, selectedTemplate);
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 3ab0ef11da9..d6e60adec9f 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
@@ -56,6 +56,19 @@
Admin
+
+ { ->
+ def advancedContentFormField = useCase.getField("advanced_content_form")
+ 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]
+ String nextViewTaskId = useCase.tasks.find { taskPair -> taskPair.transition == "open_case_settings"}?.task
+ if (nextViewTaskId != null) {
+ taskIds.add(nextViewTaskId)
+ }
+ change advancedContentFormField value { taskIds }
+ }
+
{
com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete,
@@ -435,22 +448,6 @@
task-list
-
- get
-
-
- advanced_content_form: f.advanced_content_form;
- String nextViewTaskId = useCase.tasks.find { taskPair -> taskPair.transition == "open_case_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
@@ -570,6 +567,14 @@
true
+
+ finish
+
+
+ initializeAdvancedForm()
+
+
+ data_sync
@@ -795,7 +800,7 @@
view_configuration_type
- editable
+ visible0
@@ -806,36 +811,6 @@
material
outline
-
- 0
-
-
- 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()) {
- 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 { [] }
- change view_configuration_all_data_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] }
- change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] }
-
-
- view_configuration_form
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 1044e970802..02ba2a2e066 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -48,6 +48,25 @@
adminAdmin
+
+ { ->
+ def advancedContentFormField = useCase.getField("advanced_content_form")
+ def taskIds = [useCase.tasks.find { taskPair -> taskPair.transition == "role_settings"}.task,
+ useCase.tasks.find { taskPair -> taskPair.transition == "view_settings"}.task]
+
+ def viewConfigurationIdDataField = useCase.getDataField("view_configuration_id")
+ if (viewConfigurationIdDataField.value != null && !viewConfigurationIdDataField.value.isEmpty()) {
+ def viewCase = workflowService.findOne(viewConfigurationIdDataField.value[0])
+ def taskIdsFromView = viewCase.getFieldValue("advanced_content_form")
+ if (taskIdsFromView != null) {
+ taskIds.addAll(taskIdsFromView)
+ make ["advanced_content_form"], hidden on (viewCase.tasks.findAll { it.transition == "settings" }?.task as List) when { true }
+ }
+ }
+
+ change advancedContentFormField value { taskIds }
+ }
+
{ ->
@@ -630,16 +649,6 @@
task-list
-
- get
-
-
- 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] }
-
-
- configuration_templates
@@ -888,6 +897,12 @@
finish
+
+ def outcome = menuItemService.handleConfigurationTemplate(useCase)
+ outcome.mapping.each { fieldId, value ->
+ change useCase.getField(fieldId) value { value }
+ }
+
name: f.menu_name,
identifier: f.menu_item_identifier,
@@ -915,11 +930,10 @@
view_configuration_type], hidden on trans when { true }
}
+
+
- def outcome = menuItemService.handleConfigurationTemplate(useCase)
- outcome.mapping.each { fieldId, value ->
- change useCase.getField(fieldId) value { value }
- }
+ initializeAdvancedForm()
Create
@@ -1395,6 +1409,11 @@
}
+
+
+ initializeAdvancedForm()
+
+
diff --git a/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml b/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
index c951d9b2a10..07bf4098f50 100644
--- a/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
@@ -37,6 +37,12 @@
Admin
+
+ { ->
+ def advancedContentFormField = useCase.getField("advanced_content_form")
+ change advancedContentFormField value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task] }
+ }
+
{
com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete,
@@ -174,15 +180,6 @@
task-list
-
- get
-
-
- advanced_content_form: f.advanced_content_form;
- change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task] }
-
-
- show_page_header
@@ -225,6 +222,14 @@
true
+
+ finish
+
+
+ initializeAdvancedForm()
+
+
+ data_sync
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 07b4c103e61..769e0c9788c 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
@@ -47,6 +47,12 @@
Admin
+
+ { ->
+ def advancedContentFormField = useCase.getField("advanced_content_form")
+ change advancedContentFormField value { [useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] }
+ }
+
{
com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete,
@@ -90,7 +96,7 @@
view_configuration_type
- Pick view type
+ View typemenuItemService.getAvailableViewsAsOptions(true, useCase.processIdentifier)
@@ -127,15 +133,6 @@
task-list
-
- get
-
-
- advanced_content_form: f.advanced_content_form;
- change advanced_content_form value { [useCase.tasks.find { taskPair -> taskPair.transition == "next_view_settings"}.task] }
-
-
-
@@ -145,7 +142,7 @@
Aktualizovať zobrazenie s vybraným filtromFilterSúčasný filter
- Vybrať zobrazenie
+ ZobrazenieNastavenie
@@ -153,7 +150,7 @@
FilterAktueller FilterAktualisiere die Ansicht mit dem ausgewählten Filter
- Wählen Sie einen Ansichtstyp
+ Der AnsichtstypEinstellungen
@@ -169,6 +166,14 @@
true
+
+ finish
+
+
+ initializeAdvancedForm()
+
+
+ data_sync
@@ -255,7 +260,7 @@
view_configuration_type
- editable
+ visible0
@@ -266,36 +271,6 @@
material
outline
-
- 0
-
-
- 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()) {
- 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 { [] }
- change view_configuration_all_data_form value { [] }
- return
- }
-
- def configurationCase = createCase(view_configuration_type.value + "_configuration")
- 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] }
- change view_configuration_all_data_form value { [configurationCase.tasks.find { it.transition == "all_menu_data" }.task] }
-
-
- view_configuration_form
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 5ffa5aa3732..a867954ff63 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
@@ -37,6 +37,13 @@
Admin
+
+ { ->
+ def advancedContentFormField = useCase.getField("advanced_content_form")
+ change advancedContentFormField value { [useCase.tasks.find { taskPair -> taskPair.transition == "filter_settings"}.task,
+ useCase.tasks.find { taskPair -> taskPair.transition == "header_settings"}.task] }
+ }
+
{
com.netgrif.application.engine.petrinet.domain.dataset.EnumerationMapField filterAutocomplete,
@@ -304,16 +311,6 @@
task-list
-
- get
-
-
- 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] }
-
-
- task_empty_content_text
@@ -413,6 +410,14 @@
true
+
+ finish
+
+
+ initializeAdvancedForm()
+
+
+ data_sync
From fabf8d86c39f0596c797a48721d0e90ae5b008ea Mon Sep 17 00:00:00 2001
From: chvostek
Date: Wed, 20 May 2026 12:19:23 +0200
Subject: [PATCH 086/174] [ETASK-23] Dynamic view configuration - change menu
item process identifier constant - add duplicity check on create item
transition
---
.../engine/menu/domain/MenuItemConstants.java | 2 ++
.../engine/menu/service/MenuItemService.java | 22 +++++++++----------
.../engine-processes/menu/menu_item.xml | 9 ++++++++
.../engine/action/MenuItemApiTest.groovy | 16 +++++++-------
4 files changed, 30 insertions(+), 19 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 a8405551449..9ce6aef7081 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
@@ -35,4 +35,6 @@ public class MenuItemConstants {
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";
+
+ public static final String PROCESS_IDENTIFIER = "menu_item";
}
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 fdcb7c86717..3b3f43d618e 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
@@ -110,7 +110,7 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce
if (newName == null) {
newName = new I18nString(body.getIdentifier());
}
- Case menuItemCase = createCase(FilterRunner.MENU_NET_IDENTIFIER, newName.getDefaultValue(),
+ Case menuItemCase = createCase(MenuItemConstants.PROCESS_IDENTIFIER, newName.getDefaultValue(),
loggedUser.transformToLoggedUser());
menuItemCase.setUriNodeId(uriService.findByUri(body.getUri()).getStringId());
menuItemCase = workflowService.save(menuItemCase);
@@ -205,8 +205,8 @@ public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecut
@Override
public Case findMenuItem(String identifier) {
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);
+ MenuItemConstants.PROCESS_IDENTIFIER, MenuItemConstants.FIELD_IDENTIFIER, identifier);
+ return findCase(MenuItemConstants.PROCESS_IDENTIFIER, query);
}
/**
@@ -221,8 +221,8 @@ public Case findMenuItem(String identifier) {
public Case findMenuItem(String uri, String name) {
UriNode uriNode = uriService.findByUri(uri);
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);
+ MenuItemConstants.PROCESS_IDENTIFIER, name, uriNode.getStringId());
+ return findCase(MenuItemConstants.PROCESS_IDENTIFIER, query);
}
/**
@@ -235,8 +235,8 @@ public Case findMenuItem(String uri, String name) {
@Override
public Case findFolderCase(UriNode node) {
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);
+ MenuItemConstants.PROCESS_IDENTIFIER, MenuItemConstants.FIELD_NODE_PATH, node.getUriPath());
+ return findCase(MenuItemConstants.PROCESS_IDENTIFIER, query);
}
/**
@@ -249,8 +249,8 @@ public Case findFolderCase(UriNode node) {
@Override
public boolean existsMenuItem(String identifier) {
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;
+ MenuItemConstants.PROCESS_IDENTIFIER, MenuItemConstants.FIELD_IDENTIFIER, identifier);
+ return countCases(MenuItemConstants.PROCESS_IDENTIFIER, query) > 0;
}
/**
@@ -339,7 +339,7 @@ public Case duplicateItem(Case originItem, I18nString newTitle, String newIdenti
duplicatedViewCase = duplicateView(originViewCase);
}
- Case duplicated = createCase(FilterRunner.MENU_NET_IDENTIFIER, newTitle.getDefaultValue(),
+ Case duplicated = createCase(MenuItemConstants.PROCESS_IDENTIFIER, newTitle.getDefaultValue(),
userService.getLoggedOrSystem().transformToLoggedUser());
duplicated.setUriNodeId(originItem.getUriNodeId());
duplicated.setDataSet(originItem.getDataSet());
@@ -684,7 +684,7 @@ protected Case getOrCreateFolderRecursive(UriNode node, MenuItemBody body, Case
return folderCase;
}
- folderCase = createCase(FilterRunner.MENU_NET_IDENTIFIER, body.getMenuName().getDefaultValue(),
+ folderCase = createCase(MenuItemConstants.PROCESS_IDENTIFIER, body.getMenuName().getDefaultValue(),
loggedUser.transformToLoggedUser());
folderCase.setUriNodeId(node.getParentId());
folderCase = workflowService.save(folderCase);
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 02ba2a2e066..bcca6ee0711 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -897,6 +897,15 @@
finish
+
+ identifier: f.menu_item_identifier;
+ String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"",
+ com.netgrif.application.engine.menu.domain.MenuItemConstants.PROCESS_IDENTIFIER,
+ com.netgrif.application.engine.menu.domain.MenuItemConstants.FIELD_IDENTIFIER, identifier.value)
+ if (countCasesElastic(query) > 1) {
+ throw new IllegalArgumentException("Such menu item already exists. Please, use different identifier")
+ }
+
def outcome = menuItemService.handleConfigurationTemplate(useCase)
outcome.mapping.each { fieldId, value ->
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 3c591ddd80d..01f4166931a 100644
--- a/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy
+++ b/src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy
@@ -113,11 +113,11 @@ class MenuItemApiTest {
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]
+ Case testFolder = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/netgrif/test\"", PageRequest.of(0, 1))[0]
+ Case netgrifFolder = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_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.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/\"", PageRequest.of(0, 1))[0]
+ Case rootFolder = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue.keyword:\"/\"", PageRequest.of(0, 1))[0]
assert testFolder != null && testNode != null
assert testFolder.uriNodeId == testNode.stringId
@@ -212,7 +212,7 @@ class MenuItemApiTest {
Thread.sleep(2000)
UriNode node = uriService.findByUri("/netgrif2")
- Case folderCase = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif2\"", PageRequest.of(0, 1))[0]
+ Case folderCase = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif2\"", PageRequest.of(0, 1))[0]
assert viewCase.uriNodeId == node.stringId
ArrayList childIds = folderCase.dataSet[MenuItemConstants.FIELD_CHILD_ITEM_IDS].value as ArrayList
@@ -237,11 +237,11 @@ class MenuItemApiTest {
])
Thread.sleep(2000)
- 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]
+ folderCase = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif/test3\"", PageRequest.of(0, 1))[0]
+ Case folderCase2 = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_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.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif/test3/netgrif2\"", PageRequest.of(0, 1))[0]
+ folderCase = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_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
@@ -344,7 +344,7 @@ class MenuItemApiTest {
Case leafItemCase = getMenuItem(apiCase)
sleep(2000)
- Case testFolder = findCasesElastic("processIdentifier:$FilterRunner.MENU_NET_IDENTIFIER AND dataSet.${MenuItemConstants.FIELD_NODE_PATH}.textValue:\"/netgrif/test\"", PageRequest.of(0, 1))[0]
+ Case testFolder = findCasesElastic("processIdentifier:$MenuItemConstants.PROCESS_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)
From 4d05301ce862b6badd71aabea4cb5ff64e78ae17 Mon Sep 17 00:00:00 2001
From: chvostek
Date: Wed, 20 May 2026 14:52:44 +0200
Subject: [PATCH 087/174] [ETASK-23] Dynamic view configuration - implement
menu item data index creation and usage
---
.../engine/startup/MongoDbRunner.groovy | 29 ++++++++++++--
.../engine/menu/domain/MenuItemConstants.java | 3 ++
.../engine/menu/service/MenuItemService.java | 30 +++++++++-----
.../engine-processes/menu/menu_item.xml | 39 +++++++++++++------
4 files changed, 76 insertions(+), 25 deletions(-)
diff --git a/src/main/groovy/com/netgrif/application/engine/startup/MongoDbRunner.groovy b/src/main/groovy/com/netgrif/application/engine/startup/MongoDbRunner.groovy
index 4fac0dc985e..bdfd9e709ee 100644
--- a/src/main/groovy/com/netgrif/application/engine/startup/MongoDbRunner.groovy
+++ b/src/main/groovy/com/netgrif/application/engine/startup/MongoDbRunner.groovy
@@ -1,5 +1,7 @@
package com.netgrif.application.engine.startup
+import com.netgrif.application.engine.menu.domain.MenuItemConstants
+import com.netgrif.application.engine.workflow.domain.Case
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
@@ -7,6 +9,8 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Profile
import org.springframework.data.mapping.context.MappingContext
import org.springframework.data.mongodb.core.MongoTemplate
+import org.springframework.data.mongodb.core.index.CompoundIndexDefinition
+import org.springframework.data.mongodb.core.index.IndexDefinition
import org.springframework.data.mongodb.core.index.IndexOperations
import org.springframework.data.mongodb.core.index.IndexResolver
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver
@@ -48,23 +52,40 @@ class MongoDbRunner extends AbstractOrderedCommandLineRunner {
if (host != null && port != null)
log.info("Dropping Mongo database ${host}:${port}/${name}")
else if (uri != null)
- log.info("Droppiung Mongo database ${uri}")
+ log.info("Dropping Mongo database ${uri}")
mongoTemplate.getDb().drop()
}
if (resolveIndexesOnStartup) {
+ log.info("Ensuring Mongo indexes")
resolveIndexes()
}
}
- void resolveIndexes() {
+ private void resolveIndexes() {
MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> mappingContext = mongoTemplate.getConverter().getMappingContext()
IndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext)
mappingContext.getPersistentEntities()
.stream()
.filter(it -> it.isAnnotationPresent(Document.class))
.forEach(it -> {
- IndexOperations indexOps = mongoTemplate.indexOps(it.getType());
- resolver.resolveIndexFor(it.getType()).forEach(indexOps::ensureIndex);
+ IndexOperations indexOps = mongoTemplate.indexOps(it.getType())
+ resolver.resolveIndexFor(it.getType()).forEach(indexOps::ensureIndex)
})
+ customMenuItemDataIndexes().each { indexKey, indexName ->
+ org.bson.Document keys = new org.bson.Document()
+ .append("processIdentifier", 1)
+ .append(indexKey, 1)
+ IndexDefinition index = new CompoundIndexDefinition(keys)
+ .named(indexName)
+ .background()
+ mongoTemplate.indexOps(Case.class).ensureIndex(index)
+ }
+ }
+
+ private static Map customMenuItemDataIndexes() {
+ return Map.of(
+ "dataSet.${MenuItemConstants.FIELD_IDENTIFIER}.value" as String, MenuItemConstants.IDENTIFIER_INDEX_NAME,
+ "dataSet.${MenuItemConstants.FIELD_NODE_PATH}.value" as String, MenuItemConstants.NODE_PATH_INDEX_NAME
+ )
}
}
\ No newline at end of file
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 9ce6aef7081..a6870e3d5ff 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
@@ -37,4 +37,7 @@ public class MenuItemConstants {
public static final String TRANS_ALL_MENU_DATA = "all_menu_data";
public static final String PROCESS_IDENTIFIER = "menu_item";
+
+ public static final String IDENTIFIER_INDEX_NAME = "menuItemIdentifierIdx";
+ public static final String NODE_PATH_INDEX_NAME = "menuItemNodePathIdx";
}
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 3b3f43d618e..dba913d53ca 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
@@ -29,6 +29,9 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.*;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.*;
@@ -43,6 +46,7 @@ public class MenuItemService implements IMenuItemService {
protected final IUserService userService;
protected final IUriService uriService;
protected final IElasticCaseService elasticCaseService;
+ protected final MongoTemplate mongoTemplate;
protected static final String DEFAULT_FOLDER_ICON = "folder";
@@ -204,9 +208,14 @@ public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecut
* */
@Override
public Case findMenuItem(String identifier) {
- String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"",
- MenuItemConstants.PROCESS_IDENTIFIER, MenuItemConstants.FIELD_IDENTIFIER, identifier);
- return findCase(MenuItemConstants.PROCESS_IDENTIFIER, query);
+ Query query = Query.query(
+ Criteria.where("processIdentifier").is(MenuItemConstants.PROCESS_IDENTIFIER)
+ .and(String.format("dataSet.%s.value", MenuItemConstants.FIELD_IDENTIFIER)).is(identifier)
+ );
+ query.withHint(MenuItemConstants.IDENTIFIER_INDEX_NAME);
+ List caseAsList = mongoTemplate.find(query, Case.class);
+ Optional caseOptional = caseAsList.stream().findFirst();
+ return caseOptional.orElse(null);
}
/**
@@ -234,9 +243,14 @@ public Case findMenuItem(String uri, String name) {
* */
@Override
public Case findFolderCase(UriNode node) {
- String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"",
- MenuItemConstants.PROCESS_IDENTIFIER, MenuItemConstants.FIELD_NODE_PATH, node.getUriPath());
- return findCase(MenuItemConstants.PROCESS_IDENTIFIER, query);
+ Query query = Query.query(
+ Criteria.where("processIdentifier").is(MenuItemConstants.PROCESS_IDENTIFIER)
+ .and(String.format("dataSet.%s.value", MenuItemConstants.FIELD_NODE_PATH)).is(node.getUriPath())
+ );
+ query.withHint(MenuItemConstants.NODE_PATH_INDEX_NAME);
+ List caseAsList = mongoTemplate.find(query, Case.class);
+ Optional caseOptional = caseAsList.stream().findFirst();
+ return caseOptional.orElse(null);
}
/**
@@ -248,9 +262,7 @@ public Case findFolderCase(UriNode node) {
* */
@Override
public boolean existsMenuItem(String identifier) {
- String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"",
- MenuItemConstants.PROCESS_IDENTIFIER, MenuItemConstants.FIELD_IDENTIFIER, identifier);
- return countCases(MenuItemConstants.PROCESS_IDENTIFIER, query) > 0;
+ return findMenuItem(identifier) != null;
}
/**
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 bcca6ee0711..0977dd0267f 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -259,6 +259,10 @@
menu_item_identifierMenu item identifier
+
+ menu_item_identifier_on_create
+ Menu item identifier
+
nodePathItem URI
@@ -801,7 +805,7 @@
0
- menu_item_identifier: f.menu_item_identifier,
+ menu_item_identifier: f.menu_item_identifier_on_create,
menu_name: f.menu_name;
if (menu_item_identifier.value != null && !menu_item_identifier.value.isEmpty()
@@ -821,7 +825,7 @@
- menu_item_identifier
+ menu_item_identifier_on_createeditablerequired
@@ -834,6 +838,23 @@
material
outline
+
+ 0
+
+
+ menu_item_identifier: f.menu_item_identifier_on_create;
+
+ if (menuItemService.existsMenuItem(menu_item_identifier.value)) {
+ change menu_item_identifier validations { validation('''regex $.^''',
+ new com.netgrif.application.engine.petrinet.domain.I18nString("Such menu item already exists",
+ [sk: "Taká položka menu už existuje", de: "Ein solcher Menüeintrag existiert bereits"]))
+ }
+ } else {
+ change menu_item_identifier validations { null }
+ }
+
+
+ move_dest_uri
@@ -897,15 +918,6 @@
finish
-
- identifier: f.menu_item_identifier;
- String query = String.format("processIdentifier:%s AND dataSet.%s.textValue.keyword:\"%s\"",
- com.netgrif.application.engine.menu.domain.MenuItemConstants.PROCESS_IDENTIFIER,
- com.netgrif.application.engine.menu.domain.MenuItemConstants.FIELD_IDENTIFIER, identifier.value)
- if (countCasesElastic(query) > 1) {
- throw new IllegalArgumentException("Such menu item already exists. Please, use different identifier")
- }
-
def outcome = menuItemService.handleConfigurationTemplate(useCase)
outcome.mapping.each { fieldId, value ->
@@ -914,8 +926,11 @@
name: f.menu_name,
- identifier: f.menu_item_identifier,
+ menu_item_identifier: f.menu_item_identifier,
+ menu_item_identifier_on_create: f.menu_item_identifier_on_create;
dest: f.move_dest_uri;
+
+ change menu_item_identifier value { menu_item_identifier_on_create.value }
String newUri = dest.value.join("/")
newUri = newUri.replace("//","/")
From f42843e150c3c4c53948512f6e2c5b724c3eea39 Mon Sep 17 00:00:00 2001
From: chvostek
Date: Wed, 20 May 2026 16:59:51 +0200
Subject: [PATCH 088/174] [ETASK-23] Dynamic view configuration - menu item
identifier and uri fixes - refactor MenuItemService.updateMenuItem - fix
MenuItemService.findMenuItem - improve identifier behavior on creation task
---
.../engine/menu/service/MenuItemService.java | 115 +++---------------
.../engine/menu/utils/MenuItemUtils.java | 28 +++++
.../engine-processes/menu/menu_item.xml | 4 +
3 files changed, 51 insertions(+), 96 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 dba913d53ca..519071e3893 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
@@ -103,10 +103,9 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce
log.debug("Creation of menu item case with identifier [{}] started.", body.getIdentifier());
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));
+ if (existsMenuItem(body.getIdentifier())) {
+ throw new IllegalArgumentException(String.format("Menu item identifier %s is not unique!", body.getIdentifier()));
}
Case parentItemCase = getOrCreateFolderItem(body.getUri());
@@ -121,7 +120,7 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce
parentItemCase = appendChildCaseIdAndSave(parentItemCase, menuItemCase.getStringId());
- String nodePath = createNodePath(body.getUri(), sanitizedIdentifier);
+ String nodePath = createNodePath(body.getUri(), body.getIdentifier());
uriService.getOrCreate(nodePath, UriContentType.CASE);
Case viewCase = null;
@@ -135,28 +134,20 @@ public Case createMenuItem(MenuItemBody body) throws TransitionNotExecutableExce
}
/**
- * Updates menu item case and it's configuration cases
+ * Updates menu item case and it's configuration cases (recreates)
*
* @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)
+ * @return recreated menu item case (configuration cases are recreated, but not returned)
* */
@Override
public Case updateMenuItem(Case itemCase, MenuItemBody body) throws TransitionNotExecutableException {
validateMenuItemBody(body);
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);
- itemCase = workflowService.save(itemCase);
- }
-
- Case viewCase = findView(itemCase);
- viewCase = handleView(viewCase, body.getView(), body.isUseTabbedView());
- ToDataSetOutcome dataSetOutcome = body.toDataSet(viewCase);
- itemCase = setData(itemCase, MenuItemConstants.TRANS_SYNC_ID, dataSetOutcome.getDataSet());
+ workflowService.deleteCase(itemCase);
+ itemCase = createMenuItem(body);
log.debug("Updated menu item case [{}] with identifier [{}].", itemCase.getStringId(), body.getIdentifier());
return itemCase;
}
@@ -215,7 +206,7 @@ public Case findMenuItem(String identifier) {
query.withHint(MenuItemConstants.IDENTIFIER_INDEX_NAME);
List caseAsList = mongoTemplate.find(query, Case.class);
Optional caseOptional = caseAsList.stream().findFirst();
- return caseOptional.orElse(null);
+ return caseOptional.map(aCase -> workflowService.findOne(aCase.getStringId())).orElse(null);
}
/**
@@ -250,7 +241,7 @@ public Case findFolderCase(UriNode node) {
query.withHint(MenuItemConstants.NODE_PATH_INDEX_NAME);
List caseAsList = mongoTemplate.find(query, Case.class);
Optional caseOptional = caseAsList.stream().findFirst();
- return caseOptional.orElse(null);
+ return caseOptional.map(aCase -> workflowService.findOne(aCase.getStringId())).orElse(null);
}
/**
@@ -468,15 +459,21 @@ public ConfigurationTemplateOutcome handleConfigurationTemplate(Case menuItemCas
return new ConfigurationTemplateOutcome(dataSetOutcome);
}
- protected void validateMenuItemBody(MenuItemBody menuItemBody) {
- if (menuItemBody == null) {
+ protected void validateMenuItemBody(MenuItemBody body) {
+ if (body == null) {
throw new IllegalArgumentException("Input data cannot be null");
}
- if (menuItemBody.getIdentifier() == null) {
+ if (body.getIdentifier() == null) {
throw new IllegalArgumentException("Identifier cannot be null");
}
- if (menuItemBody.getUri() == null || menuItemBody.getUri().isBlank()) {
+ if (body.getUri() == null || body.getUri().isBlank()) {
throw new IllegalArgumentException("Uri cannot be null");
+ } else {
+ body.setUri(MenuItemUtils.sanitizeUriSegments(body.getUri(), uriService));
+ List uriSegments = List.of(body.getUri().split(uriService.getUriSeparator()));
+ if (uriSegments.contains(body.getIdentifier())) {
+ throw new IllegalArgumentException("Uri cannot contain this identifier");
+ }
}
}
@@ -521,10 +518,6 @@ protected Case duplicateView(Case viewCase) throws TransitionNotExecutableExcept
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);
}
@@ -538,22 +531,6 @@ protected Case findCaseInCaseRef(Case useCase, String caseRefId) {
}
}
- protected Case handleView(Case existingViewCase, ViewBody body, boolean isTabbed) throws TransitionNotExecutableException {
- if (mustUpdateView(existingViewCase, body)) {
- return updateView(existingViewCase, body, isTabbed);
- } else if (mustCreateView(existingViewCase, body)) {
- return createView(body, isTabbed);
- } else if (mustRemoveView(existingViewCase, body)) {
- removeView(existingViewCase);
- return null;
- } else if (mustRemoveAndCreateView(existingViewCase, body)) {
- removeView(existingViewCase);
- return createView(body, isTabbed);
- } else {
- return null;
- }
- }
-
protected Case createView(ViewBody body, boolean isTabbed) throws TransitionNotExecutableException {
IUser loggedUser = userService.getLoggedOrSystem();
Case viewCase = createCase(body.getViewProcessIdentifier(), body.getViewProcessIdentifier(),
@@ -579,60 +556,6 @@ protected Case createView(ViewBody body, boolean isTabbed) throws TransitionNotE
return viewCase;
}
- 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(), isTabbed);
-
- ToDataSetOutcome outcome = body.toDataSet(associatedViewCase, filterCase);
- 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 {
- 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 updateNodeInChildrenFoldersRecursive(Case parentFolder) {
List childItemIds = MenuItemUtils.getCaseIdsFromCaseRef(parentFolder, MenuItemConstants.FIELD_CHILD_ITEM_IDS);
if (childItemIds == null || childItemIds.isEmpty()) {
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 8472e838599..b7b3107a8b6 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
@@ -1,6 +1,7 @@
package com.netgrif.application.engine.menu.utils;
import com.netgrif.application.engine.menu.domain.MenuItemConstants;
+import com.netgrif.application.engine.petrinet.service.interfaces.IUriService;
import com.netgrif.application.engine.workflow.domain.Case;
import com.netgrif.application.engine.workflow.domain.TaskPair;
import com.netgrif.application.engine.menu.service.interfaces.IMenuItemService;
@@ -29,6 +30,33 @@ public static String sanitize(String input) {
.toLowerCase();
}
+ /**
+ * Sanitizes each segment of a URI path by removing diacritical marks, replacing special characters with
+ * delimiters, and converting to lowercase. The URI is split by the separator defined in the provided
+ * {@link IUriService}, each segment is sanitized individually, and then the segments are concatenated back
+ * together.
+ *
+ * @param uri the URI string to be sanitized
+ * @param uriService the service providing the URI separator configuration
+ *
+ * @return sanitized URI string with all segments processed, or null if input URI is null
+ * */
+ public static String sanitizeUriSegments(String uri, IUriService uriService) {
+ if (uri == null) {
+ return null;
+ }
+ String[] uriSegments = uri.split(uriService.getUriSeparator());
+ if (uriSegments.length == 0) {
+ return uriService.getRoot().getUriPath();
+ }
+ StringBuilder sanitizedUriBuilder = new StringBuilder();
+ for (String uriSegment : uriSegments) {
+ sanitizedUriBuilder.append(uriService.getUriSeparator());
+ sanitizedUriBuilder.append(MenuItemUtils.sanitize(uriSegment));
+ }
+ return sanitizedUriBuilder.toString().replaceAll("//", uriService.getUriSeparator());
+ }
+
/**
* Finds task id in the provided case instance by 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 0977dd0267f..a76182a6c89 100644
--- a/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
+++ b/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
@@ -851,6 +851,10 @@
}
} else {
change menu_item_identifier validations { null }
+ String sanitizedIdentifier = com.netgrif.application.engine.menu.utils.MenuItemUtils.sanitize(menu_item_identifier.value)
+ if (menu_item_identifier.value != sanitizedIdentifier) {
+ change menu_item_identifier value { sanitizedIdentifier }
+ }
}
From ca60ccf7170270d97ed2fccdb27ed884585991e4 Mon Sep 17 00:00:00 2001
From: chvostek
Date: Fri, 22 May 2026 11:45:49 +0200
Subject: [PATCH 089/174] [ETASK-23] Dynamic view configuration - remove filter
case - remove deprecated methods in ActionDelegate - update menu
configurations for direct use of filter field
---
docs/_sidebar.md | 1 -
docs/search/filter.md | 2 +
docs/search/filter_import_export.md | 172 ---
.../logic/action/ActionDelegate.groovy | 720 +----------
.../startup/DefaultFiltersRunner.groovy | 314 -----
.../engine/startup/FilterRunner.groovy | 59 -
.../engine/startup/MenuRunner.groovy | 39 +
.../engine/startup/RunnerController.groovy | 3 +-
.../engine/auth/service/UserService.java | 6 -
.../engine/menu/domain/FilterBody.java | 21 +-
.../domain/configurations/CaseViewBody.java | 5 +
.../configurations/CaseViewConstants.java | 2 +-
.../configurations/SingleTaskViewBody.java | 5 +
.../SingleTaskViewConstants.java | 1 +
.../configurations/TabbedTicketViewBody.java | 5 +
.../domain/configurations/TaskViewBody.java | 6 +-
.../configurations/TaskViewConstants.java | 1 +
.../menu/domain/configurations/ViewBody.java | 10 +-
.../domain/configurations/ViewConstants.java | 2 -
.../domain/templates/CustomViewTemplate.java | 7 +-
.../templates/SimpleCaseViewTemplate.java | 9 +-
.../templates/SimpleTaskViewTemplate.java | 9 +-
.../templates/SingleTaskViewTemplate.java | 9 +-
.../templates/TabbedCaseViewTemplate.java | 9 +-
.../templates/TabbedTaskViewTemplate.java | 9 +-
.../templates/TabbedTicketViewTemplate.java | 9 +-
.../menu/domain/templates/Template.java | 6 +-
.../engine/menu/service/MenuItemService.java | 63 +-
.../service/interfaces/IMenuItemService.java | 2 -
.../service/FilterImportExportService.java | 380 ------
.../service/MenuImportExportService.java | 383 ------
.../service/UserFilterSearchService.java | 49 -
.../IFilterImportExportService.java | 34 -
.../interfaces/IMenuImportExportService.java | 33 -
.../interfaces/IUserFilterSearchService.java | 9 -
.../engine-processes/export_filters.xml | 171 ---
.../petriNets/engine-processes/filter.xml | 1096 -----------------
.../engine-processes/import_filters.xml | 182 ---
.../menu/case_view_configuration.xml | 215 +---
.../engine-processes/menu/menu_item.xml | 2 +-
.../menu/single_task_view_configuration.xml | 225 +---
.../menu/tabbed_ticket_view_configuration.xml | 34 -
.../menu/task_view_configuration.xml | 246 +---
.../petriNets/engine-processes/org_group.xml | 41 -
.../preference_filter_item.xml | 983 ---------------
.../application/engine/TestHelper.groovy | 2 +-
.../engine/action/ActionDelegateTest.groovy | 13 +-
.../engine/action/FilterApiTest.groovy | 184 ---
.../engine/action/MenuItemApiTest.groovy | 4 +-
.../filters/FilterImportExportTest.groovy | 546 --------
.../engine/menu/MenuImportExportTest.groovy | 14 +-
.../engine/menu/MenuItemServiceTest.java | 1 -
52 files changed, 141 insertions(+), 6212 deletions(-)
delete mode 100644 docs/search/filter_import_export.md
delete mode 100644 src/main/groovy/com/netgrif/application/engine/startup/DefaultFiltersRunner.groovy
delete mode 100644 src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy
create mode 100644 src/main/groovy/com/netgrif/application/engine/startup/MenuRunner.groovy
delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/service/UserFilterSearchService.java
delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFilterImportExportService.java
delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java
delete mode 100644 src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IUserFilterSearchService.java
delete mode 100644 src/main/resources/petriNets/engine-processes/export_filters.xml
delete mode 100644 src/main/resources/petriNets/engine-processes/filter.xml
delete mode 100644 src/main/resources/petriNets/engine-processes/import_filters.xml
delete mode 100644 src/main/resources/petriNets/engine-processes/preference_filter_item.xml
delete mode 100644 src/test/groovy/com/netgrif/application/engine/action/FilterApiTest.groovy
delete mode 100644 src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index 0bd5aacf956..d4ef204c9a9 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -8,7 +8,6 @@
* [Search]()
* [Elastic](search/elastic_mapping.md)
* [Filter](search/filter.md)
- * [Import / Export](search/filter_import_export.md)
* [Permissions]()
* [User list](roles/userlist.md)
* [Permissions](roles/permissions.md)
diff --git a/docs/search/filter.md b/docs/search/filter.md
index 0d9924921b2..454ce9f2cbd 100644
--- a/docs/search/filter.md
+++ b/docs/search/filter.md
@@ -42,6 +42,8 @@ field component.
## Filter process
+[//]: # (todo 23)
+
The engine filter process is located under `resources/petriNets/engine-processes/filter.xml`

diff --git a/docs/search/filter_import_export.md b/docs/search/filter_import_export.md
deleted file mode 100644
index cd98e559025..00000000000
--- a/docs/search/filter_import_export.md
+++ /dev/null
@@ -1,172 +0,0 @@
-# Filter import/export
-
-## User guide
-
-This guide is aimed at application users that wish to know, how to import/export selected filters from/to application.
-If you are developer that wishes to know more about the implementation and various ways the system can be interacted
-with, you can find this information in the Developer guide of filter import/export.
-
-### Overview
-
-Every user in the application have option to import/export filters. For every user in application case for import
-filters and also case for export filters exists as shown on the next image.
-
-
-
-
-
-
-
-### Processes
-
-#### Export filters
-
-After user opens **Export filters USERNAME** case, task to export filters is displayed. The task consists of three
-fields, from which only two are initially displayed to user.
-
-
-
-
-
-
-
-First field is multi choice field, which allows user to select multiple filters, which he wants to export. Filters that
-are displayed in this multi choice are all **public** filters in the application (filters which can be created by other
-users) and also all **private** filters of currently logged user. After user selects filters which he wants to export
-and clicks on the **Export filters** button, the third field is displayed.
-
-
-
-This field holds exported xml file, which contains exported filters in the custom xml format. User can download this
-file by clicking on its name. This file can be later used to import filters into the application. Users should never
-create or modify these files on their own, because of the complexity of filters format inside xml file.
-
-#### Import filters
-
-After user opens **Import filters USERNAME** case, task to import filters is displayed. The task consists of two fields,
-which are initially displayed to user and after these two fields there is one section for every imported filter.
-
-
-
-
-
-
-
-First field is file field, where user should upload xml file, that contains exported filters. After user uploads this
-xml file and clicks on **Import filters** button, application validate uploaded file against schema, which doesn't allow
-upload of files in incorrect format. If format of uploaded file is correct, application initialize creation of filters
-and displays these filters under initially displayed fields.
-
-
-
-Each section after horizontal line represents one imported filter. User can change two properties for each imported
-filter. First one is filter name and the second one is visibility of the filter. User can also see type of the filter (
-if the filter is for tasks or for cases) and also filter preview, so he can know what it is filtering.
-
-In some situation, imported filter have dependency on some processes. To correctly display preview of filter and further
-correct function of filter, all these processes need to be uploaded in application. If some of these processes are
-missing, list of missing processes is displayed inside textarea as shown on next image.
-
-
-
-When user is satisfied with imported filters, their names and visibility, he can confirm creation of these filters by
-pressing **FINISH** button on the bottom right. If the imported filters are not according to his ideas, he can abort
-further creation of these filters by pressing **CANCEL** button or deleting uploaded xml file from file field.
-
-## Developer guide
-
-This guide is aimed at developers that wish to know the implementation detail of filter import/export, so they can
-interact with or override its implementation. If you are application user and wish to know how to use the options
-available to you read the User guide of filter import/export. Please read **Filter process guide** before u start
-reading this one, because of some references on this process.
-
-### Overview
-
-The filter import and export consists of two processes, which are independent of each other. Names of processes are
-self-explaining:
-
-- **Export of filters** - serves for export of filters into xml file `export_filters.xml`
-- **Import of filters** - serves for import of filters from xml file `import_filters.xml`
-
-New schema, which describes format of xml file for exported filters, were introduced
-`filter_export_schema.xml`. All imported xml files containing filters are validated against this schema to ensure
-filters will be correctly created and working. Also, new exception class for incorrect xml file format of imported
-filters was created to throw error after validating xml file against schema `IllegalFilterFileException`.
-
-Multiple classes were added to support serialization and deserialization of filters. These classes are:
-
-- Configuration
-- FilterImportExport
-- FilterImportExportList
-- FilterMetadataExport
-- Predicate
-- PredicateArray
-- PredicateValue
-- CustomFilterDeserializer
-
-`ActionDelegate` have two new functions, which support filters import/export:
-`exportFilters(Collection filtersToExport)` and `importFilters()`. Also, new service `FilterImportExportService`
-which is called by these two functions was created and which performs most functionality in importing and exporting
-functions.
-
-When creating new user, case for importing filters and also case for exporting filters is created for this user.
-
-Also, new property was added into `application.properties`, with name `nae.filter.export.file-name`, which defines name
-of xml file that contains exported filters. This property has default string value of **filters.xml**.
-
-When filters are exported, all of their ancestors are exported as well. If the exported filters have common ancestors,
-the ancestors are only exported once. The relationships between filters are reconstructed on import based on the
-exported filters case Ids. It is assumed that parents precede their children in the imported file
-(the exported file is generated in this way). If a filter cannot find its parent on import the connection will
-be severed and an error will be logged.
-
-### Processes
-
-#### Export filters
-
-Export process consists of one task `exportFilter`, which carry out whole exporting of filters. Process and also the
-only task consist of three data fields. First one is `exportable_filters`, which is **multichoice_map** that displays
-all filters, that are exportable by currently logged user. List is loaded from `ActionDelegate`
-method `findAllFilters()` and returns all filter cases, that are public and created by any user or that are private and
-created by currently logged user.
-
-After selecting filters to export and clicking on second data field **button** with id `export_btn`, the
-method `exportFilters()` from `ActionDelegate`
-is called. This method serialize selected filters into xml file and put this file into third **file** field with
-id `export_file`.
-
-Export filters can be run any number of times so the user can export any number of filters in selected combinations into
-any number of xml files.
-
-#### Import filters
-
-Import process also consists of one task `importFilter`, which carry out whole importing of filters. There are also just
-three data fields in this process. First one is **file** field `upload_file` that serves for upload of xml file with
-filters. After uploading this file and clicking on the next **button** field `import_file`, the `importFilters()` method
-from `ActionDelegate` is called, that validates uploaded xml file against xsd schema.
-
-- If file is valid, method creates **Filter** cases via **Automated new filter** task and move them into **Import
- filter** task. Method returns list of **Import filter** task ids, that are set as value for **taskRef**
- field `imported_filters`, so the user can update some values of these filters.
-- If file is not valid, error message is thrown.
-
-Pressing **FINISH** button in this task moves imported filters into state, where they can be added into group
-navigation.
-
-Pressing **CANCEL** button or changing value to `upload_file` deletes created filters, so there will not be unworkable
-filters saved in the application database.
-
-As well as export filters, import filters can be run any number of times, so user can upload how many filters as he
-wants.
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 e1bc462a28c..8c7007dedce 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
@@ -49,8 +49,7 @@ import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetServi
import com.netgrif.application.engine.petrinet.service.interfaces.IProcessRoleService
import com.netgrif.application.engine.petrinet.service.interfaces.IUriService
import com.netgrif.application.engine.rules.domain.RuleRepository
-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.utils.FullPageRequest
import com.netgrif.application.engine.workflow.domain.Case
@@ -162,18 +161,9 @@ class ActionDelegate {
@Autowired
Scheduler scheduler
- @Autowired
- IUserFilterSearchService filterSearchService
-
@Autowired
IConfigurableMenuService configurableMenuService
- @Autowired
- IMenuImportExportService menuImportExportService
-
- @Autowired
- IFilterImportExportService filterImportExportService
-
@Autowired
IExportService exportService
@@ -1395,25 +1385,6 @@ class ActionDelegate {
return new DynamicValidation(rule, message)
}
- List findFilters(String userInput) {
- return filterSearchService.autocompleteFindFilters(userInput)
- }
-
- List findAllFilters() {
- return filterSearchService.autocompleteFindFilters("")
- }
-
- FileFieldValue exportFilters(Collection filtersToExport) {
- if (filtersToExport.isEmpty()) {
- return null
- }
- return filterImportExportService.exportFiltersToFile(filtersToExport)
- }
-
- List importFilters() {
- return filterImportExportService.importFilters()
- }
-
File exportCasesToFile(Closure predicate, String pathName, ExportDataConfig config = null,
int pageSize = exportConfiguration.getMongoPageSize()) {
File exportFile = new File(pathName)
@@ -1556,318 +1527,6 @@ class ActionDelegate {
return findTasks(requests, loggedUser, page, pageSize, locale, isIntersection)
}
- List findDefaultFilters() {
- if (!createDefaultFilters) {
- return []
- }
- return findCases({ it.processIdentifier.eq(FilterRunner.FILTER_PETRI_NET_IDENTIFIER).and(it.author.id.eq(userService.system.stringId)) })
- }
-
- /**
- * Creates filter instance of type {@value DefaultFiltersRunner#FILTER_TYPE_CASE}
- *
- * @param title filter case title
- * @param query elastic query for the view
- * @param icon filter case icon
- * @param allowedNets List of process identifiers
- * @param visibility Possible values: {@value DefaultFiltersRunner#FILTER_VISIBILITY_PRIVATE} or {@value DefaultFiltersRunner#FILTER_VISIBILITY_PUBLIC}
- * @param filterMetadata metadata for filter. If no value is provided, then default value is used: {@link #defaultFilterMetadata(String)}
- *
- * @return created {@link Case} instance of filter
- */
- @NamedVariant
- Case createCaseFilter(def title, String query, List allowedNets,
- String icon = "", String visibility = DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE, def filterMetadata = null) {
- return createFilter(title, query, DefaultFiltersRunner.FILTER_TYPE_CASE, allowedNets, icon, visibility, filterMetadata)
- }
-
- /**
- * Creates filter instance of type {@value DefaultFiltersRunner#FILTER_TYPE_TASK}
- *
- * @param title filter case title
- * @param query elastic query for the view
- * @param icon filter case icon
- * @param allowedNets List of process identifiers
- * @param visibility Possible values: {@value DefaultFiltersRunner#FILTER_VISIBILITY_PRIVATE} or {@value DefaultFiltersRunner#FILTER_VISIBILITY_PUBLIC}
- * @param filterMetadata metadata for filter. If no value is provided, then default value is used: {@link #defaultFilterMetadata(String)}
- *
- * @return created {@link Case} instance of filter
- */
- @NamedVariant
- Case createTaskFilter(def title, String query, List allowedNets,
- String icon = "", String visibility = DefaultFiltersRunner.FILTER_VISIBILITY_PRIVATE, def filterMetadata = null) {
- return createFilter(title, query, DefaultFiltersRunner.FILTER_TYPE_TASK, allowedNets, icon, visibility, filterMetadata)
- }
-
- /**
- * Creates filter instance.
- *
- * @param title filter case title
- * @param query elastic query for the view
- * @param type Filter type. Possible values: {@value DefaultFiltersRunner#FILTER_TYPE_CASE} or {@value DefaultFiltersRunner#FILTER_TYPE_TASK}
- * @param icon filter case icon
- * @param allowedNets List of process identifiers
- * @param visibility Possible values: {@value DefaultFiltersRunner#FILTER_VISIBILITY_PRIVATE} or {@value DefaultFiltersRunner#FILTER_VISIBILITY_PUBLIC}
- * @param filterMetadata metadata for filter. If no value is provided, then default value is used: {@link #defaultFilterMetadata(String)}
- *
- * @return created {@link Case} instance of filter
- */
- @NamedVariant
- Case createFilter(def title, String query, String type, List allowedNets,
- String icon, String visibility, def filterMetadata) {
- 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)
- }
-
- /**
- * Changes data of provided filter instance. These attributes can be changed:
- *
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}.
+ *
If the value cannot be converted to task IDs, this method returns an empty list.
*
* @param taskRef field whose value contains task IDs
* {@link com.netgrif.application.engine.petrinet.domain.dataset.FieldType#TASK_REF},
@@ -1316,7 +1316,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 task IDs, this method returns {@code null}.
+ *
If the value cannot be converted to task IDs, this method returns an empty list.