提交 dceac8f0 编写于 作者: J Jesse Glick

[FIXED JENKINS-19173] Added ContextMenuVisibility marker interface for Action’s.

上级 549e23ba
......@@ -58,6 +58,9 @@ Upcoming changes</a>
<li class=bug>
Pass full list of all possible jobs to ViewJobFilter when recurse option is set
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20143">issue 20143</a>)
<li class=rfe>
Added API allowing plugins to hide entries from the context menu even while they appear in the sidepanel.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19173">issue 19173</a>)
</li>
</ul>
</div><!--=TRUNK-END=-->
......
......@@ -153,6 +153,7 @@ import com.google.common.base.Predicates;
import java.util.concurrent.atomic.AtomicLong;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Utility functions used in views.
......@@ -1848,4 +1849,14 @@ public class Functions {
rsp.setHeader("X-Jenkins-CLI-Host", TcpSlaveAgentListener.CLI_HOST_NAME);
}
}
@Restricted(NoExternalUse.class) // for actions.jelly and ContextMenu.add
public static boolean isContextMenuVisible(Action a) {
if (a instanceof ModelObjectWithContextMenu.ContextMenuVisibility) {
return ((ModelObjectWithContextMenu.ContextMenuVisibility) a).isVisible();
} else {
return true;
}
}
}
......@@ -6,7 +6,6 @@ import hudson.model.Action;
import hudson.model.Actionable;
import hudson.model.BallColor;
import hudson.model.Computer;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.ModelObject;
import hudson.model.Node;
......@@ -58,7 +57,7 @@ public interface ModelObjectWithContextMenu extends ModelObject {
/**
* Data object that represents the context menu.
*
*
* Via {@link HttpResponse}, this class is capable of converting itself to JSON that &lt;l:breadcrumb/> understands.
*/
@ExportedBean
......@@ -83,8 +82,14 @@ public interface ModelObjectWithContextMenu extends ModelObject {
add(a);
return this;
}
/**
* @see ContextMenuVisibility
*/
public ContextMenu add(Action a) {
if (!Functions.isContextMenuVisible(a)) {
return this;
}
StaplerRequest req = Stapler.getCurrentRequest();
String text = a.getDisplayName();
String base = Functions.getIconFilePath(a);
......@@ -95,7 +100,7 @@ public interface ModelObjectWithContextMenu extends ModelObject {
return add(url,icon,text);
}
public ContextMenu add(String url, String icon, String text) {
if (text != null && icon != null && url != null)
items.add(new MenuItem(url,icon,text));
......@@ -314,4 +319,22 @@ public interface ModelObjectWithContextMenu extends ModelObject {
return withDisplayName(o.getDisplayName());
}
}
/**
* Allows an action to decide whether it will be visible in a context menu.
* @since 1.538
*/
interface ContextMenuVisibility extends Action {
/**
* Determines whether to show this action right now.
* Can always return false, for an action which should never be in the context menu;
* or could examine {@link Stapler#getCurrentRequest}.
* @return true to display it, false to hide
* @see ContextMenu#add(Action)
*/
boolean isVisible();
}
}
......@@ -39,7 +39,7 @@ THE SOFTWARE.
<st:include page="action.jelly" from="${action}" optional="true">
<j:if test="${action.iconFileName!=null}">
<l:task icon="${h.getIconFilePath(action)}" title="${action.displayName}"
href="${h.getActionUrl(it.url,action)}" />
href="${h.getActionUrl(it.url,action)}" contextMenu="${h.isContextMenuVisible(action)}"/>
</j:if>
</st:include>
</j:forEach>
......
/*
* The MIT License
*
* Copyright 2013 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.model;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.FreeStyleProject;
import hudson.model.TransientProjectActionFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import static jenkins.model.ModelObjectWithContextMenu.*;
import static org.junit.Assert.assertEquals;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.For;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.Stapler;
@For(ContextMenu.class)
public class ContextMenuTest {
@Rule public JenkinsRule j = new JenkinsRule();
@Bug(19173)
@Test public void contextMenuVisibility() throws Exception {
final FreeStyleProject p = j.createFreeStyleProject("p");
Callable<ContextMenu> doContextMenu = new Callable<ContextMenu>() {
@Override public ContextMenu call() throws Exception {
return p.doContextMenu(Stapler.getCurrentRequest(), Stapler.getCurrentResponse());
}
};
ActionFactory f = j.jenkins.getExtensionList(TransientProjectActionFactory.class).get(ActionFactory.class);
f.visible = true;
ContextMenu menu = j.executeOnServer(doContextMenu);
Map<String,String> parsed = parse(menu);
assertEquals(parsed.toString(), "Hello", parsed.get("testing"));
f.visible = false;
menu = j.executeOnServer(doContextMenu);
parsed = parse(menu);
assertEquals(parsed.toString(), null, parsed.get("testing"));
}
@TestExtension public static class ActionFactory extends TransientProjectActionFactory {
boolean visible;
@SuppressWarnings("rawtypes")
@Override public Collection<? extends Action> createFor(AbstractProject target) {
return Collections.singleton(new ContextMenuVisibility() {
@Override public boolean isVisible() {
return visible;
}
@Override public String getIconFileName() {
return "whatever";
}
@Override public String getDisplayName() {
return "Hello";
}
@Override public String getUrlName() {
return "testing";
}
});
}
}
private static Map<String,String> parse(ContextMenu menu) {
Map<String,String> r = new TreeMap<String,String>();
for (MenuItem mi : menu.items) {
r.put(mi.url.replaceFirst("^.*/(.)", "$1"), mi.displayName);
}
return r;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册