Skip to content

Commit ffeb72e

Browse files
committed
[JENKINS-75683] re-introduce actions for Jenkins with custom action.jelly
adjust the task taglib so that it renders in the style of the header when in the header. remove the workaround from the HEader (setting the url) we now correctly set it to the Jenkins instance for the header (root) actions. Nested tasks work, but when expanded the original looses its tooltip. Contains a (non working) experiment of using a jump list for the embedded actions but for some reason this is not working... needs investigation.
1 parent 8c4038d commit ffeb72e

File tree

3 files changed

+167
-74
lines changed

3 files changed

+167
-74
lines changed

core/src/main/java/jenkins/views/Header.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,5 @@ public List<Action> getActions() {
8787
).reversed())
8888
.toList();
8989
}
90-
91-
@Restricted(NoExternalUse.class)
92-
public String getUrl() {
93-
// tasks (using in actions) use it.URL and it is Header here.
94-
// so return the root URL of Jenkins
95-
// XXX HACK HACK - need to check why the tasks taglib calculates URLs as ${JENKINS_URL}/null/action.url
96-
// should probably set ${it} to Jenkins in the jelly instead but this hack is quick and easy for now
97-
return "";
98-
}
90+
9991
}

core/src/main/resources/lib/layout/header/actions.jelly

Lines changed: 67 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,77 +12,81 @@
1212
<j:choose>
1313
<j:when test="${h.hasView(action, 'action')}">
1414
<x:comment>${action.class.name} using action view</x:comment>
15-
<st:include page="action.jelly" from="${action}" optional="true"/>
15+
<j:scope>
16+
<!-- Root actions expect to be in the context of Jenkins (as it) -->
17+
<j:set var="it" value="${app}"/>
18+
<j:set var="isInHeader" value="true"/>
19+
<st:include page="action.jelly" from="${action}" optional="true"/>
20+
</j:scope>
1621
</j:when>
1722
<j:otherwise>
18-
<j:set var="icon" value="${action.iconClassName != null ? action.iconClassName : action.iconFileName}"/>
19-
<j:if test="${icon == null}">
20-
<x:comment>${action.class.name} has no icon</x:comment>
21-
</j:if>
22-
<!-- there was no custom action and we have no icon so this is intended to be hidden -->
23-
<j:if test="${icon != null}">
24-
<x:comment>${action.class.name} has an icon</x:comment>
25-
26-
<j:set var="isCurrent" value="${h.hyperlinkMatchesCurrentPage(action.urlName)}" />
27-
28-
<j:set var="jumplist">
29-
<st:include it="${action}" page="jumplist.jelly" optional="true" />
30-
</j:set>
31-
32-
<j:if test="${jumplist.length() == 0}">
33-
<j:set var="tooltip">
34-
<st:include it="${action}" page="tooltip.jelly" optional="true" />
35-
</j:set>
23+
<j:scope>
24+
<j:set var="icon" value="${action.iconClassName != null ? action.iconClassName : action.iconFileName}"/>
25+
<j:if test="${icon == null}">
26+
<x:comment>${action.class.name} has no icon</x:comment>
3627
</j:if>
37-
38-
<j:set var="badge" value="${action.badge}" />
39-
<j:if test="${jumplist.length() == 0 and tooltip.length() == 0}">
40-
<j:set var="tooltip">
41-
<div style="text-align: center;">${action.displayName}</div>
42-
<j:if test="${badge != null}">
43-
<div style="text-align: center; color: var(--text-color-secondary)">${badge.tooltip}</div>
44-
</j:if>
28+
<!-- there was no custom action and we have no icon so this is intended to be hidden -->
29+
<j:if test="${icon != null}">
30+
<x:comment>${action.class.name} has an icon</x:comment>
31+
32+
<j:set var="isCurrent" value="${h.hyperlinkMatchesCurrentPage(action.urlName)}" />
33+
34+
<j:set var="jumplist">
35+
<st:include it="${action}" page="jumplist.jelly" optional="true" />
4536
</j:set>
46-
</j:if>
47-
48-
<j:set var="interactive" value="${jumplist.length() gt 0}" />
49-
<x:element name="${action.urlName == null ? 'button' : 'a'}">
50-
<x:attribute name="data-dropdown">${interactive}</x:attribute>
51-
<x:attribute name="id">root-action-${action.class.simpleName}</x:attribute>
52-
<x:attribute name="href">${h.getActionUrl(app.url, action)}</x:attribute>
53-
<j:if test="${interactive}">
54-
<x:attribute name="data-tippy-offset">[0, 10]</x:attribute>
37+
38+
<j:if test="${jumplist.length() == 0}">
39+
<j:set var="tooltip">
40+
<st:include it="${action}" page="tooltip.jelly" optional="true" />
41+
</j:set>
5542
</j:if>
56-
<j:if test="${!interactive}">
57-
<x:attribute name="data-html-tooltip" escapeText="false"><j:out value="${tooltip}" /></x:attribute>
43+
44+
<j:set var="badge" value="${action.badge}" />
45+
<j:if test="${jumplist.length() == 0 and tooltip.length() == 0}">
46+
<j:set var="tooltip">
47+
<div style="text-align: center;">${action.displayName}</div>
48+
<j:if test="${badge != null}">
49+
<div style="text-align: center; color: var(--text-color-secondary)">${badge.tooltip}</div>
50+
</j:if>
51+
</j:set>
5852
</j:if>
59-
<x:attribute name="data-tooltip-interactive">${interactive}</x:attribute>
60-
<x:attribute name="data-tippy-animation">tooltip</x:attribute>
61-
<x:attribute name="data-tippy-theme">${interactive ? 'dropdown' : 'tooltip'}</x:attribute>
62-
<x:attribute name="data-tippy-trigger">mouseenter focus</x:attribute>
63-
<x:attribute name="data-tippy-touch">${interactive}</x:attribute>
64-
<x:attribute name="data-type">header-action</x:attribute>
65-
<x:attribute name="draggable">false</x:attribute>
66-
<x:attribute name="class">jenkins-button ${isCurrent ? '' : 'jenkins-button--tertiary'}</x:attribute>
67-
<l:icon src="${icon}" class="${action.class.name == 'jenkins.model.navigation.UserAction' ? 'jenkins-avatar' : ''}"/>
68-
<span class="jenkins-visually-hidden" data-type="action-label">${action.displayName}</span>
69-
<j:if test="${badge != null}">
70-
<span class="jenkins-badge jenkins-!-${badge.severity}-color" />
53+
54+
<j:set var="interactive" value="${jumplist.length() gt 0}" />
55+
<x:element name="${action.urlName == null ? 'button' : 'a'}">
56+
<x:attribute name="data-dropdown">${interactive}</x:attribute>
57+
<x:attribute name="id">root-action-${action.class.simpleName}</x:attribute>
58+
<x:attribute name="href">${h.getActionUrl(app.url, action)}</x:attribute>
59+
<j:if test="${interactive}">
60+
<x:attribute name="data-tippy-offset">[0, 10]</x:attribute>
61+
</j:if>
62+
<j:if test="${!interactive}">
63+
<x:attribute name="data-html-tooltip" escapeText="false"><j:out value="${tooltip}" /></x:attribute>
64+
</j:if>
65+
<x:attribute name="data-tooltip-interactive">${interactive}</x:attribute>
66+
<x:attribute name="data-tippy-animation">tooltip</x:attribute>
67+
<x:attribute name="data-tippy-theme">${interactive ? 'dropdown' : 'tooltip'}</x:attribute>
68+
<x:attribute name="data-tippy-trigger">mouseenter focus</x:attribute>
69+
<x:attribute name="data-tippy-touch">${interactive}</x:attribute>
70+
<x:attribute name="data-type">header-action</x:attribute>
71+
<x:attribute name="draggable">false</x:attribute>
72+
<x:attribute name="class">jenkins-button ${isCurrent ? '' : 'jenkins-button--tertiary'}</x:attribute>
73+
<l:icon src="${icon}" class="${action.class.name == 'jenkins.model.navigation.UserAction' ? 'jenkins-avatar' : ''}"/>
74+
<span class="jenkins-visually-hidden" data-type="action-label">${action.displayName}</span>
75+
<j:if test="${badge != null}">
76+
<span class="jenkins-badge jenkins-!-${badge.severity}-color" />
77+
</j:if>
78+
</x:element>
79+
80+
<j:if test="${interactive}">
81+
<template>
82+
<div class="jenkins-dropdown">
83+
<j:out value="${jumplist}" />
84+
</div>
85+
</template>
7186
</j:if>
72-
</x:element>
73-
74-
<j:if test="${interactive}">
75-
<template>
76-
<div class="jenkins-dropdown">
77-
<j:out value="${jumplist}" />
78-
</div>
79-
</template>
8087
</j:if>
81-
82-
<j:set var="jumplist" />
83-
<j:set var="tooltip" />
84-
</j:if>
85-
<!-- </st:include> -->
88+
</j:scope>
89+
8690
</j:otherwise>
8791
</j:choose>
8892
</j:forEach>

core/src/main/resources/lib/layout/task.jelly

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ THE SOFTWARE.
2323
-->
2424

2525
<?jelly escape-by-default='true'?>
26-
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout">
26+
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:x="jelly:xml">
2727
<st:documentation> <![CDATA[
2828
This tag inside <l:tasks> tag renders the left navigation bar of Jenkins.
2929
Each <task> tag gets an icon and link.
@@ -117,13 +117,20 @@ THE SOFTWARE.
117117
</j:if>
118118

119119
<j:if test="${attrs.permissions != null}">
120-
<j:set var="hasPermission" value="${h.hasAnyPermission(it, attrs.permissions)}" />
120+
<!-- some actions set permissions with a single permission :-o -->
121+
<j:if test="${attrs.permissions.class.isArray()}">
122+
<j:set var="hasPermission" value="${h.hasAnyPermission(it, attrs.permissions)}" />
123+
</j:if>
124+
<j:if test="${!attrs.permissions.class.isArray()}">
125+
<j:set var="hasPermission" value="${h.hasPermission(it, attrs.permissions)}" />
126+
</j:if>
121127
</j:if>
122128

123129
<j:if test="${attrs.permission == null and attrs.permissions == null}">
124130
<j:set var="hasPermission" value="true" />
125131
</j:if>
126132

133+
127134
<j:if test="${hasPermission}">
128135
<j:choose>
129136
<j:when test="${parentTagContext!=null}">
@@ -133,9 +140,11 @@ THE SOFTWARE.
133140
</j:when>
134141
<j:set var="match" value="${isCurrent or hasMatchingChild}" />
135142

143+
<!--
136144
<j:if test="${attrs.permission != null}">
137145
<j:set var="hasPermission" value="${h.hasPermission(it, attrs.permission)}" />
138146
</j:if>
147+
-->
139148

140149
<!--
141150
The icon attribute can be an icon class spec, or can be the url of an icon image.
@@ -147,6 +156,94 @@ THE SOFTWARE.
147156
icon class spec from the icon metadata, otherwise we create an icon element instance using
148157
icon attribute value as an icon "src" (Vs class spec).
149158
-->
159+
160+
<!-- <x:comment>${action.class.name} isInHader: {$isInHeader} enabled: ${enabled}</x:comment> -->
161+
<j:when test="${isInHeader==true}">
162+
<j:scope>
163+
164+
<!-- we are rendering inside the modern header this is for compatability, so we do not need to worry about jumplists which was introduced in the new header -->
165+
<!-- <x:comment>${action.class.name} rendering inside task.jelly</x:comment> -->
166+
<j:if test="${enabled != false}"> <!-- no rendering at all for disabled tasks in the header -->
167+
168+
<!-- there was no custom action and we have no icon so this is intended to be hidden -->
169+
<j:if test="${icon != null}">
170+
<!-- <x:comment>${action.class.name} has an icon</x:comment> -->
171+
172+
<j:set var="isCurrent" value="${h.hyperlinkMatchesCurrentPage(action.urlName)}" />
173+
174+
175+
<j:set var="jumplist">
176+
<j:if test="${match}">
177+
<d:invokeBody />
178+
</j:if>
179+
</j:set>
180+
181+
<!-- <x:comment escapeText="true">jumplist is xxxx${jumplist}xxxx</x:comment> -->
182+
183+
184+
<j:if test="${jumplist.length() == 0}">
185+
<j:set var="tooltip">
186+
<div style="text-align: center;">${title}</div>
187+
<j:if test="${badge != null}">
188+
<div style="text-align: center; color: var(--text-color-secondary)">${badge.tooltip}</div>
189+
</j:if>
190+
</j:set>
191+
</j:if>
192+
193+
<j:set var="interactive" value="${jumplist.length() gt 0}" />
194+
<x:element name="${action.urlName == null ? 'button' : 'a'}">
195+
<x:attribute name="data-dropdown">${interactive}</x:attribute>
196+
<x:attribute name="id">root-action-${action.class.simpleName}</x:attribute>
197+
<j:if test="${requiresConfirmation}">
198+
<x:attribute name="href">#</x:attribute>
199+
<x:attribute name="data-url">${href}</x:attribute>
200+
<x:attribute name="data-post">${post}</x:attribute>
201+
<x:attribute name="data-message">${confirmationMessage}</x:attribute>
202+
<x:attribute name="data-title">${title}</x:attribute>
203+
<x:attribute name="data-destructive">${destructive}</x:attribute>
204+
</j:if>
205+
<j:if test="${!requiresConfirmation}">
206+
<x:attribute name="href">${href}</x:attribute>
207+
</j:if>
208+
<j:if test="${interactive}">
209+
<x:attribute name="data-tippy-offset">[0, 10]</x:attribute>
210+
</j:if>
211+
<j:if test="${!interactive}">
212+
<x:attribute name="data-html-tooltip" escapeText="false"><j:out value="${tooltip}" /></x:attribute>
213+
</j:if>
214+
<x:attribute name="data-tooltip-interactive">${interactive}</x:attribute>
215+
<x:attribute name="data-tippy-animation">tooltip</x:attribute>
216+
<x:attribute name="data-tippy-theme">${interactive ? 'dropdown' : 'tooltip'}</x:attribute>
217+
<x:attribute name="data-tippy-trigger">mouseenter focus</x:attribute>
218+
<x:attribute name="data-tippy-touch">${interactive}</x:attribute>
219+
<x:attribute name="data-type">header-action</x:attribute>
220+
<x:attribute name="draggable">false</x:attribute>
221+
<x:attribute name="class">jenkins-button ${isCurrent ? '' : 'jenkins-button--tertiary'} ${requiresConfirmation ? 'confirmation-link' : ''}</x:attribute>
222+
<l:icon src="${icon}" class="''"/>
223+
<span class="jenkins-visually-hidden" data-type="action-label">${title}</span>
224+
<j:if test="${badge != null}">
225+
<span class="jenkins-badge jenkins-!-${badge.severity}-color" />
226+
</j:if>
227+
</x:element>
228+
229+
<j:if test="${interactive}">
230+
<template>
231+
<div class="jenkins-dropdown">
232+
<j:out value="${jumplist}" />
233+
</div>
234+
</template>
235+
</j:if>
236+
</j:if>
237+
238+
<!-- TODO should we just use the jumplist rather than add on icons ?-->
239+
<j:if test="${match}">
240+
<!-- include the subtasks -->
241+
<d:invokeBody />
242+
</j:if>
243+
</j:if>
244+
</j:scope>
245+
</j:when>
246+
<!-- regular sidebar not the new header -->
150247
<j:when test="${enabled==false}">
151248
<div class="task disabled">
152249
<span class="task-link-wrapper">

0 commit comments

Comments
 (0)