Skip to content

Commit 9f93292

Browse files
committed
Add visibility options for record view custom menu items
1 parent a45dcfc commit 9f93292

11 files changed

Lines changed: 238 additions & 80 deletions

File tree

domain/src/main/java/org/fao/geonet/domain/page/Page.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public class Page extends GeonetEntity implements Serializable {
6767

6868
private String label;
6969
private String icon;
70+
private boolean showOnNonApproved = true;
71+
private boolean showOnApproved = true;
72+
private boolean showWhenWorkflowDisabled = true;
7073

7174
public Page() {
7275

@@ -155,6 +158,33 @@ public String getIcon() {
155158
return icon;
156159
}
157160

161+
@Column(nullable = false, columnDefinition = "boolean default true")
162+
public boolean isShowOnNonApproved() {
163+
return showOnNonApproved;
164+
}
165+
166+
public void setShowOnNonApproved(boolean showOnNonApproved) {
167+
this.showOnNonApproved = showOnNonApproved;
168+
}
169+
170+
@Column(nullable = false, columnDefinition = "boolean default true")
171+
public boolean isShowOnApproved() {
172+
return showOnApproved;
173+
}
174+
175+
public void setShowOnApproved(boolean showOnApproved) {
176+
this.showOnApproved = showOnApproved;
177+
}
178+
179+
@Column(nullable = false, columnDefinition = "boolean default true")
180+
public boolean isShowWhenWorkflowDisabled() {
181+
return showWhenWorkflowDisabled;
182+
}
183+
184+
public void setShowWhenWorkflowDisabled(boolean showWhenWorkflowDisabled) {
185+
this.showWhenWorkflowDisabled = showWhenWorkflowDisabled;
186+
}
187+
158188
/**
159189
* Get all the page's groups.
160190
*

services/src/main/java/org/fao/geonet/api/pages/PageProperties.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public class PageProperties implements Serializable {
2525
private String icon;
2626
private Page.PageFormat format;
2727
private List<String> groups;
28+
private boolean showOnNonApproved = true;
29+
private boolean showOnApproved = true;
30+
private boolean showWhenWorkflowDisabled = true;
2831
private Page page;
2932

3033
public PageProperties() {
@@ -40,6 +43,9 @@ public PageProperties(Page p) {
4043
status = p.getStatus();
4144
label = p.getLabel();
4245
icon = p.getIcon();
46+
showOnNonApproved = p.isShowOnNonApproved();
47+
showOnApproved = p.isShowOnApproved();
48+
showWhenWorkflowDisabled = p.isShowWhenWorkflowDisabled();
4349
if (CollectionUtils.isNotEmpty(p.getGroups())) {
4450
groups = new ArrayList<>();
4551
for (Group g : p.getGroups()) {
@@ -132,4 +138,28 @@ public List<String> getGroups() {
132138
public void setGroups(List<String> groups) {
133139
this.groups = groups;
134140
}
141+
142+
public boolean isShowOnNonApproved() {
143+
return showOnNonApproved;
144+
}
145+
146+
public void setShowOnNonApproved(boolean showOnNonApproved) {
147+
this.showOnNonApproved = showOnNonApproved;
148+
}
149+
150+
public boolean isShowOnApproved() {
151+
return showOnApproved;
152+
}
153+
154+
public void setShowOnApproved(boolean showOnApproved) {
155+
this.showOnApproved = showOnApproved;
156+
}
157+
158+
public boolean isShowWhenWorkflowDisabled() {
159+
return showWhenWorkflowDisabled;
160+
}
161+
162+
public void setShowWhenWorkflowDisabled(boolean showWhenWorkflowDisabled) {
163+
this.showWhenWorkflowDisabled = showWhenWorkflowDisabled;
164+
}
135165
}

services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ private ResponseEntity<String> createPage(PageProperties pageProperties,
213213
}
214214
}
215215

216+
newPage.setShowOnNonApproved(pageProperties.isShowOnNonApproved());
217+
newPage.setShowOnApproved(pageProperties.isShowOnApproved());
218+
newPage.setShowWhenWorkflowDisabled(pageProperties.isShowWhenWorkflowDisabled());
219+
216220
pageRepository.save(newPage);
217221
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
218222
} else {
@@ -296,6 +300,9 @@ private ResponseEntity<Void> updatePageInternal(@NotNull String language,
296300
newIcon != null ? newIcon : pageToUpdate.getIcon(),
297301
CollectionUtils.isNotEmpty(_groups)? _groups: null);
298302

303+
pageCopy.setShowOnNonApproved(pageProperties.isShowOnNonApproved());
304+
pageCopy.setShowOnApproved(pageProperties.isShowOnApproved());
305+
pageCopy.setShowWhenWorkflowDisabled(pageProperties.isShowWhenWorkflowDisabled());
299306
pageRepository.save(pageCopy);
300307
pageRepository.delete(pageToUpdate);
301308
} else {
@@ -305,6 +312,9 @@ private ResponseEntity<Void> updatePageInternal(@NotNull String language,
305312
pageToUpdate.setStatus(pageProperties.getStatus() != null ? pageProperties.getStatus() : pageToUpdate.getStatus());
306313
pageToUpdate.setLabel(newLabel);
307314
pageToUpdate.setIcon(newIcon);
315+
pageToUpdate.setShowOnNonApproved(pageProperties.isShowOnNonApproved());
316+
pageToUpdate.setShowOnApproved(pageProperties.isShowOnApproved());
317+
pageToUpdate.setShowWhenWorkflowDisabled(pageProperties.isShowWhenWorkflowDisabled());
308318

309319
pageToUpdate.getGroups().clear();
310320
if (pageToUpdate.getStatus() == Page.PageStatus.GROUPS || pageToUpdate.getStatus() == Page.PageStatus.GROUPS_AND_ADMIN) {

web-ui/src/main/resources/catalog/components/pages/GnStaticPagesDirective.js

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,107 @@
140140
// Set button style based on explicit parameter (defaults to false)
141141
$scope.renderAsButton = $scope.renderAsButton === "true";
142142

143+
function shouldShowPage(page) {
144+
if (!page) return false;
145+
if (
146+
$scope.section === "record_view_menu" &&
147+
$scope.context &&
148+
$scope.context.currentRecord
149+
) {
150+
var currentRecord = $scope.context.currentRecord;
151+
if (!currentRecord.isWorkflowEnabled())
152+
return page.showWhenWorkflowDisabled === true;
153+
if (currentRecord.mdStatus == 2) return page.showOnApproved === true;
154+
return page.showOnNonApproved === true;
155+
}
156+
return true;
157+
}
158+
159+
function rebuildMenu() {
160+
if (!$scope.pages) {
161+
return;
162+
}
163+
164+
$scope.pagesMenu = [];
165+
buildMenu($scope.pagesMenu, $scope.pages, $scope.pagesConfig);
166+
167+
if ($scope.pagesMenu.length === 1) {
168+
$scope.page = $scope.pagesMenu[0];
169+
$scope.isSubmenu = $scope.page.type === "submenu";
170+
$scope.isExternalLink =
171+
$scope.page.format === "LINK" ||
172+
$scope.page.format === "EMAILLINK" ||
173+
$scope.page.format === "HTMLPAGE";
174+
175+
if (
176+
$scope.page.format === "EMAILLINK" &&
177+
$scope.page.link &&
178+
!$scope.page.link.startsWith("mailto:")
179+
) {
180+
$scope.page.link = "mailto:" + $scope.page.link;
181+
}
182+
} else {
183+
$scope.page = null;
184+
}
185+
}
186+
187+
function buildMenu(pagesMenu, staticPages, pagesConfig) {
188+
pagesConfig.forEach(function (menu) {
189+
if (typeof menu === "string") {
190+
if (staticPages[menu]) {
191+
if (shouldShowPage(staticPages[menu])) {
192+
pagesMenu.push(staticPages[menu]);
193+
}
194+
} else {
195+
console.warn(
196+
menu +
197+
" not found in pages configuration." +
198+
" Check your UI configuration."
199+
);
200+
}
201+
} else if (angular.isObject(menu)) {
202+
var key = Object.keys(menu)[0];
203+
var value = menu[key];
204+
205+
var submenu = {
206+
label: key,
207+
icon: undefined,
208+
type: "submenu",
209+
pages: []
210+
};
211+
212+
var menuItems;
213+
214+
// If the submenu is using the legacy array format
215+
if (angular.isArray(value)) {
216+
menuItems = value;
217+
// If the submenu is using the new object format with an items array and optional icon
218+
} else if (angular.isObject(value)) {
219+
if (angular.isArray(value.items)) {
220+
menuItems = value.items;
221+
}
222+
if (angular.isDefined(value.icon)) {
223+
submenu.icon = value.icon;
224+
}
225+
} else {
226+
console.warn(
227+
"Invalid menu configuration for " +
228+
key +
229+
". Expected an array of page identifiers or an object with an items array."
230+
);
231+
return;
232+
}
233+
234+
buildMenu(submenu.pages, staticPages, menuItems);
235+
236+
if (submenu.pages.length > 0) {
237+
pagesMenu.push(submenu);
238+
}
239+
}
240+
});
241+
return pagesMenu;
242+
}
243+
143244
if ($scope.pagesConfig.length > 0) {
144245
gnStaticPagesService.loadPages($scope.language, $scope.section).then(
145246
function (response) {
@@ -149,28 +250,21 @@
149250
$scope.pages[page.pageId] = page;
150251
});
151252

152-
gnStaticPagesService.buildMenu(
153-
$scope.pagesMenu,
154-
$scope.pages,
155-
$scope.pagesConfig
156-
);
157-
if ($scope.pagesMenu.length === 1) {
158-
$scope.page = $scope.pagesMenu[0];
159-
$scope.isSubmenu = $scope.page.type === "submenu";
160-
$scope.isExternalLink =
161-
$scope.page.format === "LINK" ||
162-
$scope.page.format === "EMAILLINK" ||
163-
$scope.page.format === "HTMLPAGE";
164-
165-
if ($scope.page.format === "EMAILLINK") {
166-
$scope.page.link = "mailto:" + $scope.page.link;
167-
}
168-
}
253+
rebuildMenu();
169254
},
170255
function (response) {
171256
$scope.pagesList = null;
172257
}
173258
);
259+
260+
// Rebuild visibility-dependent menu items when context or any nested value changes.
261+
$scope.$watch(
262+
"context",
263+
function () {
264+
rebuildMenu();
265+
},
266+
true
267+
);
174268
}
175269
}
176270
};

web-ui/src/main/resources/catalog/components/pages/GnStaticPagesService.js

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -39,61 +39,8 @@
3939
});
4040
};
4141

42-
function buildMenu(pagesMenu, staticPages, pagesConfig) {
43-
pagesConfig.forEach(function (menu) {
44-
if (typeof menu === "string") {
45-
if (staticPages[menu]) {
46-
pagesMenu.push(staticPages[menu]);
47-
} else {
48-
console.warn(
49-
menu +
50-
" not found in pages configuration." +
51-
" Check your UI configuration."
52-
);
53-
}
54-
} else if (angular.isObject(menu)) {
55-
var key = Object.keys(menu)[0];
56-
var value = menu[key];
57-
58-
var submenu = {
59-
label: key,
60-
icon: undefined,
61-
type: "submenu",
62-
pages: []
63-
};
64-
65-
var menuItems;
66-
67-
// If the submenu is using the legacy array format
68-
if (angular.isArray(value)) {
69-
menuItems = value;
70-
// If the submenu is using the new object format with an items array and optional icon
71-
} else if (angular.isObject(value)) {
72-
if (angular.isArray(value.items)) {
73-
menuItems = value.items;
74-
}
75-
if (angular.isDefined(value.icon)) {
76-
submenu.icon = value.icon;
77-
}
78-
} else {
79-
console.warn(
80-
"Invalid menu configuration for " +
81-
key +
82-
". Expected an array of page identifiers or an object with an items array."
83-
);
84-
return;
85-
}
86-
87-
buildMenu(submenu.pages, staticPages, menuItems);
88-
pagesMenu.push(submenu);
89-
}
90-
});
91-
return pagesMenu;
92-
}
93-
9442
return {
95-
loadPages: loadPages,
96-
buildMenu: buildMenu
43+
loadPages: loadPages
9744
};
9845
}
9946
]);

web-ui/src/main/resources/catalog/components/pages/partials/menu-page.html

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
<li
2-
data-ng-class="{'dropdown': isSubmenu, 'btn-group': renderAsButton}"
3-
role="menuitem"
4-
data-ng-hide="isSubmenu && page.pages.length < 1"
5-
>
1+
<li data-ng-class="{'dropdown': isSubmenu, 'btn-group': renderAsButton}" role="menuitem">
62
<!-- Submenu dropdown toggle -->
73
<a
84
data-ng-if="isSubmenu"

web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,8 +2527,8 @@
25272527
function () {
25282528
return function (url, context) {
25292529
return url
2530-
.replace(/{uuid}/g, (context && context.uuid) || "")
2531-
.replace(/{id}/g, (context && context.id) || "");
2530+
.replace(/{uuid}/g, (context && context.currentRecord && context.currentRecord.uuid) || "")
2531+
.replace(/{id}/g, (context && context.currentRecord && context.currentRecord.id) || "");
25322532
};
25332533
}
25342534
]);

web-ui/src/main/resources/catalog/js/admin/StaticPagesController.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@
171171
status: "HIDDEN",
172172
groups: "",
173173
label: "",
174-
sections: []
174+
sections: [],
175+
showOnNonApproved: true,
176+
showOnApproved: true,
177+
showWhenWorkflowDisabled: true
175178
};
176179

177180
$scope.pageApiLink = "";

web-ui/src/main/resources/catalog/locales/en-v4.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,11 @@
429429
"pageLink": "Link",
430430
"pageLink-help": "URL can contain the following variables between '{' and '}': <ul><li><strong>uuid</strong> for the current metadata uuid</li><li><strong>id</strong> for the current metadata id</li></ul>",
431431
"pageSection-help": "Currently, the default UI view only supports TOP, RECORD_VIEW_MENU, and FOOTER values. Custom UI views can make use of additional values.",
432+
"pageVisibilityConditions": "Record view visibility",
433+
"pageVisibility-showOnNonApproved": "Show on non-approved records (workflow enabled)",
434+
"pageVisibility-showOnApproved": "Show on approved copies (workflow enabled)",
435+
"pageVisibility-showWhenWorkflowDisabled": "Show when workflow is disabled",
436+
"pageVisibilityConditions-help": "Controls when this menu item appears in the record view. Only applies to pages in the Record View Menu section. All options checked means always visible.",
432437
"application/vnd.geo+json": "GeoJSON",
433438
"application/json": "JSON",
434439
"shape-zip": "ESRI Shapefile",

0 commit comments

Comments
 (0)