From 5d85564836246398238ba869080c10b5e0ef5a8b Mon Sep 17 00:00:00 2001 From: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:04:01 +0000 Subject: [PATCH] more --- core/src/main/java/hudson/model/View.java | 3 - .../model/ModelObjectWithContextMenu.java | 43 +++++- .../main/java/jenkins/model/menu/Group.java | 5 + .../java/jenkins/model/menu/event/Action.java | 3 + .../model/menu/event/DoNothingAction.java | 3 + .../model/menu/event/DropdownAction.java | 4 + .../jenkins/model/menu/event/LinkAction.java | 5 + .../model/view/NewProjectActionFactory.java | 48 +++++++ .../model/view/NewProjectMenuItem.java | 70 --------- .../main/js/components/dropdowns/jumplists.js | 133 +++++++++++------- 10 files changed, 191 insertions(+), 126 deletions(-) create mode 100644 core/src/main/java/jenkins/model/view/NewProjectActionFactory.java delete mode 100644 core/src/main/java/jenkins/model/view/NewProjectMenuItem.java diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index ddcc8cbc477e..77e3ef5ff448 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -1489,9 +1489,6 @@ public List getTransientActions() { // System.out.println("Ignoring " + ignored); } - System.out.println(collect); - System.out.println("----"); - return collect; } } diff --git a/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java b/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java index 113e077351e0..4de45d0bbf32 100644 --- a/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java +++ b/core/src/main/java/jenkins/model/ModelObjectWithContextMenu.java @@ -11,6 +11,10 @@ import java.util.List; import javax.servlet.ServletException; import jenkins.management.Badge; +import jenkins.model.menu.Group; +import jenkins.model.menu.Semantic; +import jenkins.model.menu.event.DropdownAction; +import jenkins.model.menu.event.LinkAction; import org.apache.commons.jelly.JellyException; import org.jenkins.ui.icon.Icon; import org.jenkins.ui.icon.IconSet; @@ -83,6 +87,20 @@ public ContextMenu add(Action action) { MenuItem menuItem = new MenuItem() .withDisplayName(action.getDisplayName()); + menuItem.badge = action.getBadge(); + menuItem.semantic = action.getSemantic(); + menuItem.group = action.getGroup(); + menuItem.action = action.getAction(); + + if (action.getAction().getClass() == LinkAction.class) { + menuItem.url = ((LinkAction)action.getAction()).getUrl(); + } + + if (action.getAction().getClass() == DropdownAction.class) { + ContextMenu cm = new ContextMenu(); + menuItem.subMenu = cm.addAll(((DropdownAction)action.getAction()).getActions()); + } + if (action.getIconFileName() != null && action.getIconFileName().startsWith("symbol-")) { menuItem.icon = action.getIconFileName(); menuItem.iconXml = Symbol.get(new SymbolRequest.Builder() @@ -230,9 +248,17 @@ class MenuItem { @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "read by Stapler") public boolean requiresConfirmation; - private Badge badge; +// @Exported(inline = true) + private Group group; + +// @Exported(inline = true) + private jenkins.model.menu.event.Action action; + +// @Exported(inline = true) + private Semantic semantic; + private String message; /** @@ -264,6 +290,21 @@ public Badge getBadge() { return badge; } + @Exported(inline = true) + public Group getGroup() { + return group; + } + + @Exported(inline = true) + public jenkins.model.menu.event.Action getAction() { + return action; + } + + @Exported + public Semantic getSemantic() { + return semantic; + } + @Exported public String getMessage() { return message; diff --git a/core/src/main/java/jenkins/model/menu/Group.java b/core/src/main/java/jenkins/model/menu/Group.java index e10ef2d901a2..15a463ffab5b 100644 --- a/core/src/main/java/jenkins/model/menu/Group.java +++ b/core/src/main/java/jenkins/model/menu/Group.java @@ -1,5 +1,9 @@ package jenkins.model.menu; +import org.kohsuke.stapler.export.Exported; +import org.kohsuke.stapler.export.ExportedBean; + +@ExportedBean public class Group { private final int order; @@ -28,6 +32,7 @@ public static Group of(int customOrder) { return new Group(customOrder); } + @Exported public int getOrder() { return order; } diff --git a/core/src/main/java/jenkins/model/menu/event/Action.java b/core/src/main/java/jenkins/model/menu/event/Action.java index 0ff0dbba556d..47a2f95f3748 100644 --- a/core/src/main/java/jenkins/model/menu/event/Action.java +++ b/core/src/main/java/jenkins/model/menu/event/Action.java @@ -1,4 +1,7 @@ package jenkins.model.menu.event; +import org.kohsuke.stapler.export.ExportedBean; + +@ExportedBean public interface Action { } diff --git a/core/src/main/java/jenkins/model/menu/event/DoNothingAction.java b/core/src/main/java/jenkins/model/menu/event/DoNothingAction.java index 3cb78dbed068..4270cf70d539 100644 --- a/core/src/main/java/jenkins/model/menu/event/DoNothingAction.java +++ b/core/src/main/java/jenkins/model/menu/event/DoNothingAction.java @@ -1,3 +1,6 @@ package jenkins.model.menu.event; +import org.kohsuke.stapler.export.ExportedBean; + +@ExportedBean public final class DoNothingAction implements Action {} diff --git a/core/src/main/java/jenkins/model/menu/event/DropdownAction.java b/core/src/main/java/jenkins/model/menu/event/DropdownAction.java index 4ca490ddf0d9..4b655b281dde 100644 --- a/core/src/main/java/jenkins/model/menu/event/DropdownAction.java +++ b/core/src/main/java/jenkins/model/menu/event/DropdownAction.java @@ -3,7 +3,10 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import org.kohsuke.stapler.export.Exported; +import org.kohsuke.stapler.export.ExportedBean; +@ExportedBean public final class DropdownAction implements Action { private final List actions; @@ -16,6 +19,7 @@ public static DropdownAction of(hudson.model.Action... actions) { return new DropdownAction(Arrays.stream(actions).collect(Collectors.toList())); } + @Exported(inline = true) public List getActions() { return actions; } diff --git a/core/src/main/java/jenkins/model/menu/event/LinkAction.java b/core/src/main/java/jenkins/model/menu/event/LinkAction.java index 478d220542a4..34ad773e3a2c 100644 --- a/core/src/main/java/jenkins/model/menu/event/LinkAction.java +++ b/core/src/main/java/jenkins/model/menu/event/LinkAction.java @@ -1,5 +1,9 @@ package jenkins.model.menu.event; +import org.kohsuke.stapler.export.Exported; +import org.kohsuke.stapler.export.ExportedBean; + +@ExportedBean public final class LinkAction implements Action { private final String url; @@ -12,6 +16,7 @@ public static LinkAction of(String url) { return new LinkAction(url); } + @Exported public String getUrl() { return url; } diff --git a/core/src/main/java/jenkins/model/view/NewProjectActionFactory.java b/core/src/main/java/jenkins/model/view/NewProjectActionFactory.java new file mode 100644 index 000000000000..5dd03a1d537a --- /dev/null +++ b/core/src/main/java/jenkins/model/view/NewProjectActionFactory.java @@ -0,0 +1,48 @@ +package jenkins.model.view; + +import hudson.Extension; +import hudson.model.Action; +import hudson.model.View; +import java.util.Collection; +import java.util.Set; +import jenkins.model.TransientActionFactory; +import jenkins.model.menu.Group; +import jenkins.model.menu.event.LinkAction; + +@Extension +public class NewProjectActionFactory extends TransientActionFactory { + + @Override + public Class type() { + return View.class; + } + + @Override + public Collection createFor(View target) { + if (!target.hasPermission(View.CREATE)) { + return Set.of(); + } + + return Set.of(new Action() { + @Override + public String getDisplayName() { + return "New " + target.getNewPronoun(); + } + + @Override + public String getIconFileName() { + return "symbol-add"; + } + + @Override + public Group getGroup() { + return Group.FIRST_IN_APP_BAR; + } + + @Override + public jenkins.model.menu.event.Action getAction() { + return LinkAction.of("newJob"); + } + }); + } +} diff --git a/core/src/main/java/jenkins/model/view/NewProjectMenuItem.java b/core/src/main/java/jenkins/model/view/NewProjectMenuItem.java deleted file mode 100644 index a60da0a5fac2..000000000000 --- a/core/src/main/java/jenkins/model/view/NewProjectMenuItem.java +++ /dev/null @@ -1,70 +0,0 @@ -package jenkins.model.view; - -import hudson.Extension; -import hudson.model.Action; -import hudson.model.AllView; -import hudson.model.ListView; -import java.util.Collection; -import java.util.Set; -import jenkins.model.TransientActionFactory; -import jenkins.model.menu.Group; -import jenkins.model.menu.event.LinkAction; - -public class NewProjectMenuItem implements Action { - - @Override - public String getDisplayName() { - return "New project"; - } - - @Override - public String getIconFileName() { - return "symbol-add"; - } - - @Override - public Group getGroup() { - return Group.FIRST_IN_APP_BAR; - } - - @Override - public jenkins.model.menu.event.Action getAction() { - return LinkAction.of("newJob"); - } - - @Extension - public static class TransientActionFactoryListViewImpl extends TransientActionFactory { - - @Override - public Class type() { - return ListView.class; - } - - @Override - public Collection createFor(ListView target) { - if (!target.hasPermission(ListView.CREATE)) { - return Set.of(); - } - - return Set.of(new NewProjectMenuItem()); - } - } - - @Extension - public static class TransientActionFactoryViewImpl extends TransientActionFactory { - - @Override - public Class type() { - return AllView.class; - } - - @Override - public Collection createFor(AllView target) { - if (!target.hasPermission(AllView.CREATE)) { - return Set.of(); - } - - return Set.of(new NewProjectMenuItem()); - } - } -} diff --git a/war/src/main/js/components/dropdowns/jumplists.js b/war/src/main/js/components/dropdowns/jumplists.js index 3297da22e9ea..a8562b2ab66f 100644 --- a/war/src/main/js/components/dropdowns/jumplists.js +++ b/war/src/main/js/components/dropdowns/jumplists.js @@ -61,65 +61,94 @@ function generateDropdowns() { ); } -/* +/** + * @typedef SwagTing + * @type {object} + * @property {string} type - an ID. + * @property {string} displayName - your name. + * @property {{order: number}} group - your age. + * @property {string} icon - your name. + * @property {string} iconXml - your name. + * @property {string} url - your name. + * @property {string} post - your name. + * @property {{text: string, tooltip: string, severity: string}} badge - your name. + * @property {string} semantic - your name. + * @property {boolean} requiresConfirmation - your name. + * @property {string} message - your name. + * */ + +/** * Generates the contents for the dropdown + * @param {SwagTing[]} items */ function mapChildrenItemsToDropdownItems(items) { - return items.map((item) => { + let initialGroup = null; + return items.flatMap((item) => { if (item.type === "HEADER") { - return { - type: "HEADER", - label: item.displayName, - }; - } + return { + type: "HEADER", + label: item.displayName, + }; + } - if (item.type === "SEPARATOR") { - return { - type: "SEPARATOR", - }; - } + if (item.type === "SEPARATOR") { + return { + type: "SEPARATOR", + }; + } - return { - icon: item.icon, - iconXml: item.iconXml, - label: item.displayName, - url: item.url, - type: item.post || item.requiresConfirmation ? "button" : "link", - badge: item.badge, - onClick: () => { - if (item.post || item.requiresConfirmation) { - if (item.requiresConfirmation) { - dialog - .confirm(item.displayName, { message: item.message }) - .then(() => { - const form = document.createElement("form"); - form.setAttribute("method", item.post ? "POST" : "GET"); - form.setAttribute("action", item.url); - if (item.post) { - crumb.appendToForm(form); - } - document.body.appendChild(form); - form.submit(); - }); - } else { - fetch(item.url, { - method: "post", - headers: crumb.wrap({}), - }); - notificationBar.show( - item.displayName + ": Done.", - notificationBar.SUCCESS, - ); - } + const response = [] + + if (initialGroup != null && item.group.order !== initialGroup && item.group.order > 3) { +response.push({ + type: "SEPARATOR", +}) } - }, - subMenu: item.subMenu - ? () => { - return mapChildrenItemsToDropdownItems(item.subMenu.items); - } - : null, - }; - }); + initialGroup = item.group.order; + + response.push({ + icon: item.icon, + iconXml: item.iconXml, + label: item.displayName, + url: item.url, + type: item.post || item.requiresConfirmation ? "button" : "link", + badge: item.badge, + clazz: item.semantic ? 'jenkins-!-' + item.semantic?.toLowerCase() + '-color' : '', + onClick: () => { + if (item.post || item.requiresConfirmation) { + if (item.requiresConfirmation) { + dialog + .confirm(item.displayName, { message: item.message }) + .then(() => { + const form = document.createElement("form"); + form.setAttribute("method", item.post ? "POST" : "GET"); + form.setAttribute("action", item.url); + if (item.post) { + crumb.appendToForm(form); + } + document.body.appendChild(form); + form.submit(); + }); + } else { + fetch(item.url, { + method: "post", + headers: crumb.wrap({}), + }); + notificationBar.show( + item.displayName + ": Done.", + notificationBar.SUCCESS, + ); + } + } + }, + subMenu: item.subMenu + ? () => { + return mapChildrenItemsToDropdownItems(item.subMenu.items); + } + : null, + }); + return response; + }) } export default { init };