Skip to content

Commit

Permalink
Allow customization of Job icons
Browse files Browse the repository at this point in the history
  • Loading branch information
strangelookingnerd committed Sep 12, 2024
1 parent 65f374c commit 6f31aad
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 30 deletions.
50 changes: 50 additions & 0 deletions core/src/main/java/hudson/model/BuildStatusIcon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package hudson.model;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Renders {@link BallColor} as icon for a Job.
*
* @since TODO
*/
public class BuildStatusIcon extends JobIcon {

@DataBoundConstructor
public BuildStatusIcon() { /* NOP */ }

@Override
public String getImageOf(String size) {
return getBuildStatus().getImageOf(size);
}

@Override
public String getIconClassName() {
return "symbol-status-" + getBuildStatus().getIconName();
}

@Override
public String getDescription() {
return getBuildStatus().getDescription();
}

protected BallColor getBuildStatus() {
Job<?, ?> job = getOwner();
if (job != null) {
return job.getIconColor();
}

return BallColor.NOTBUILT;
}

@Extension
public static class DescriptorImpl extends JobIconDescriptor {

@Override
@NonNull
public String getDisplayName() {
return Messages.BuildStatusIcon_DisplayName();
}
}
}
40 changes: 40 additions & 0 deletions core/src/main/java/hudson/model/Job.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R

boolean keepDependencies;

private JobIcon icon;

/**
* List of properties configured for this project.
*/
Expand Down Expand Up @@ -246,6 +248,10 @@ public void onLoad(ItemGroup<? extends Item> parent, String name)
saveNextBuildNumber();
}

if (icon == null) {
setIcon(new BuildStatusIcon());
}

if (properties == null) // didn't exist < 1.72
properties = new CopyOnWriteList<>();

Expand Down Expand Up @@ -1256,6 +1262,32 @@ public BallColor getIconColor() {
return BallColor.NOTBUILT;
}

/**
* Gets the icon used for this job.
*
* @return the icon.
*
* @since TODO
*/
public JobIcon getIcon() {
return icon;
}

/**
* Sets the icon used for this job.
*
* @param icon the icon.
*
* @since TODO
*/
public void setIcon(JobIcon icon) {
this.icon = icon;

if (icon != null) {
icon.setOwner(this);
}
}

/**
* Get the current health report for a job.
*
Expand Down Expand Up @@ -1399,6 +1431,14 @@ public synchronized void doConfigSubmit(StaplerRequest2 req,
try (BulkChange bc = new BulkChange(this)) {
setDisplayName(json.optString("displayNameOrNull"));

JSONObject jobIconConfig = json.optJSONObject("icon");
if (jobIconConfig != null) {
JobIcon jobIcon = Descriptor.bindJSON(req, JobIcon.class, jobIconConfig);
setIcon(jobIcon);
} else {
setIcon(null);
}

logRotator = null;

DescribableList<JobProperty<?>, JobPropertyDescriptor> t = new DescribableList<>(NOOP, getAllProperties());
Expand Down
47 changes: 47 additions & 0 deletions core/src/main/java/hudson/model/JobIcon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package hudson.model;

import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.ExtensionPoint;
import jenkins.model.Jenkins;
import org.jenkins.ui.icon.IconSpec;

/**
* Renders {@link StatusIcon} for a Job.
*
* <p>
* Possible subtypes can range from dumb icons that always render the same thing to smarter icons
* that change its icon based on the state of the job.
*
* @since TODO
*/
public abstract class JobIcon extends AbstractStatusIcon implements Describable<JobIcon>, ExtensionPoint,
IconSpec {

private Job<?, ?> owner;

/**
* Called by {@link Job} to set the owner that this icon is used for.
* @param job the job.
*/
protected void setOwner(@Nullable Job<?, ?> job) {
owner = job;
}

/**
* Get the owner.
* @return the job.
*/
protected @Nullable Job<?, ?> getOwner() {
return owner;
}

@Override
public String getIconClassName() {
return null;
}

@Override
public JobIconDescriptor getDescriptor() {
return (JobIconDescriptor) Jenkins.get().getDescriptorOrDie(getClass());
}
}
27 changes: 27 additions & 0 deletions core/src/main/java/hudson/model/JobIconDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package hudson.model;

import hudson.DescriptorExtensionList;
import jenkins.model.Jenkins;

/**
* Job icon descriptor.
*
* @since TODO
*/
public abstract class JobIconDescriptor extends Descriptor<JobIcon> {

public static DescriptorExtensionList<JobIcon, JobIconDescriptor> all() {
return Jenkins.get().getDescriptorList(JobIcon.class);
}

/**
* Returns true if this {@link Job} type is applicable to the
* given job type.
* @param jobType the type of job.
* @return true to indicate applicable, in which case the icon will be
* displayed in the configuration screen of this job.
*/
public boolean isApplicable(Class<? extends Job<?, ?>> jobType) {
return true;
}
}
39 changes: 39 additions & 0 deletions core/src/main/java/hudson/model/TestIcon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package hudson.model;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Renders a test icon for a Job.
*/
public class TestIcon extends JobIcon {

@DataBoundConstructor
public TestIcon() { /* NOP */ }

@Override
public String getImageOf(String size) {
return null;
}

@Override
public String getIconClassName() {
return "symbol-edit";
}

@Override
public String getDescription() {
return "Testing";
}

@Extension
public static class DescriptorImpl extends JobIconDescriptor {

@Override
@NonNull
public String getDisplayName() {
return "Test Icon";
}
}
}
1 change: 1 addition & 0 deletions core/src/main/resources/hudson/model/Job/configure.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ THE SOFTWARE.
<f:entry title="${%Description}" help="${app.markupFormatter.helpUrl}">
<f:textarea name="description" value="${it.description}" codemirror-mode="${app.markupFormatter.codeMirrorMode}" codemirror-config="${app.markupFormatter.codeMirrorConfig}" previewEndpoint="/markupFormatter/previewDescription"/>
</f:entry>
<f:dropdownDescriptorSelector title="${%Icon}" field="icon" />
</div>

<f:descriptorList field="properties" descriptors="${h.getJobPropertyDescriptors(it)}" forceRowSet="true"/>
Expand Down
17 changes: 12 additions & 5 deletions core/src/main/resources/hudson/model/Job/index.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,18 @@ THE SOFTWARE.
<div class="jenkins-app-bar">
<div class="jenkins-app-bar__content jenkins-build-caption">
<j:set var="lastBuild" value="${it.lastBuild}" />
<j:if test="${lastBuild != null}">
<a href="${rootURL + '/' + lastBuild.url}" class="jenkins-!-display-contents" tabindex="-1">
<l:icon src="symbol-status-${lastBuild.iconColor.iconName}" tooltip="${lastBuild.iconColor.description}"/>
</a>
</j:if>
<j:choose>
<j:when test="${lastBuild != null}">
<a href="${rootURL + '/' + lastBuild.url}" class="jenkins-!-display-contents" tabindex="-1">
<l:icon src="${it.icon.iconClassName}" tooltip="${it.icon.description}" />
</a>
</j:when>
<j:otherwise>
<a href="${rootURL + '/' + it.url}" class="jenkins-!-display-contents" tabindex="-1">
<l:icon src="${it.icon.iconClassName}" tooltip="${it.icon.description}"/>
</a>
</j:otherwise>
</j:choose>
<h1 class="job-index-headline page-headline">
<l:breakable value="${it.displayName}"/>
</h1>
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/resources/hudson/model/Messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ BallColor.Success=Success
BallColor.Unstable=Unstable

Build.post_build_steps_failed=Post-build steps failed

BuildStatusIcon.DisplayName=Build Status Icon

CLI.clear-queue.shortDescription=Clears the build queue.
CLI.online-node.shortDescription=Resume using a node for performing builds, to cancel out the earlier "offline-node" command.

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/resources/hudson/model/Run/statusIcon.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ THE SOFTWARE.
<st:setHeader name="X-Executor-Remaining" value="${it.executor.estimatedRemainingTime}" />
<st:setHeader name="X-Executor-Stuck" value="${it.executor.likelyStuck}" />
<l:ajax>
<l:icon alt="${it.iconColor.description}" src="symbol-status-${it.iconColor.iconName}" class="icon-xlg" tooltip="${it.iconColor.description}"/>
<l:icon alt="${it.iconColor.description}" src="symbol-status-${it.iconColor.iconName}" class="icon-lg" tooltip="${it.iconColor.description}"/>
</l:ajax>
</j:jelly>
18 changes: 5 additions & 13 deletions core/src/main/resources/hudson/views/StatusColumn/column.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,9 @@ THE SOFTWARE.

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
<j:choose>
<j:when test="${job.iconColor.iconName != null}">
<td class="jenkins-table__cell--tight jenkins-table__icon" data="${job.iconColor.ordinal()}">
<div class="jenkins-table__cell__button-wrapper">
<l:icon src="symbol-status-${job.iconColor.iconName}" tooltip="${job.iconColor.description}" />
</div>
</td>
</j:when>
<j:otherwise>
<!-- This is for special conditions, such as folders -->
<t:ballColorTd it="${job.iconColor}" />
</j:otherwise>
</j:choose>
<td class="jenkins-table__cell--tight jenkins-table__icon">
<div class="jenkins-table__cell__button-wrapper">
<l:icon src="${job.icon.iconClassName}" tooltip="${job.icon.description}" />
</div>
</td>
</j:jelly>
2 changes: 1 addition & 1 deletion core/src/main/resources/lib/hudson/jobLink.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ THE SOFTWARE.
</st:attribute>
</st:documentation>

<l:icon alt="${job.iconColor.description}" src="symbol-status-${job.iconColor.iconName}" class="icon-sm"/>
<l:icon alt="${job.icon.description}" src="${job.icon.iconClassName}" class="icon-sm"/>
<a href="${h.getRelativeLinkTo(job)}" class="model-link jenkins-icon-adjacent">
<l:breakable value="${job.fullDisplayName}"/>
</a>
Expand Down
10 changes: 1 addition & 9 deletions core/src/main/resources/lib/hudson/projectView.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,7 @@ THE SOFTWARE.
<div class="jenkins-jobs-list__item">
<a class="jenkins-jobs-list__item__details" href="${jobBaseUrl}${job.shortUrl}">
<div class="jenkins-jobs-list__item__icons">
<j:choose>
<j:when test="${job.iconColor.iconName != null}">
<l:icon src="symbol-status-${job.iconColor.iconName}" tooltip="${job.iconColor.description}" />
</j:when>
<j:otherwise>
<!-- This is for special conditions, such as folders -->
<l:icon src="${job.iconColor.iconClassName}" />
</j:otherwise>
</j:choose>
<l:icon src="${job.icon.iconClassName}" tooltip="${job.icon.description}" />
</div>
<div class="jenkins-jobs-list__item__details__text">
<p class="jenkins-jobs-list__item__label">${job.displayName}</p>
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/resources/lib/hudson/projectViewRow.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<tr id="job_${job.name}" class="${job.disabled?'disabledJob':null} job-status-${job.iconColor.iconName}">
<tr id="job_${job.name}" class="${job.disabled?'disabledJob':null} ${job.icon.iconClassName}">
<j:forEach var="col" items="${columnExtensions}">
<st:include page="column.jelly" it="${col}"/>
</j:forEach>
Expand Down
1 change: 1 addition & 0 deletions war/src/main/scss/components/_app-bar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
text-overflow: ellipsis;

svg {
color: var(--text-color) !important;
width: 2rem !important;
height: 2rem !important;
}
Expand Down

0 comments on commit 6f31aad

Please sign in to comment.