[JENKINS-69339] badge icon for Management links (#6995)

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
Co-authored-by: Basil Crow <me@basilcrow.com>
Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com>
Co-authored-by: Tim Jacomb <timjacomb1@gmail.com>
Co-authored-by: Alexander Brandes <mc.cache@web.de>
This commit is contained in:
authored and GitHub committed 2023-01-02 18:08:44 +00:00
1 parent 989ef5b6d6
commit 622e4c6ebb
16 files changed
+194 -17

No files matched your search

@@ -57,6 +57,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.management.Badge;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
@@ -471,5 +472,14 @@ public class OldDataMonitor extends AdministrativeMonitor {
public String getDisplayName() {
return Messages.OldDataMonitor_DisplayName();
}
@Override
public Badge getBadge() {
int size = get(Jenkins.get()).data.size();
if (size > 0) {
return new Badge(Integer.toString(size), Messages.OldDataMonitor_OldDataTooltip(), Badge.Severity.WARNING);
}
return null;
}
}
}
@@ -70,7 +70,9 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
* {@code http://SERVER/jenkins/manage} page. This view should typically render
* a DIV box with class='alert alert-danger' or class='alert alert-warning' with a human-readable text
* inside it. It often also contains a link to a page that provides more details
* about the problem.
* about the problem.<br>
* Additionally 2 numbers are shown in the Jenkins header of administrators, one with the number or active
* non-security relevant monitors and one with the number of active security relevant monitors.
* </dd>
* </dl>
*
@@ -34,6 +34,7 @@ import hudson.security.Permission;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.management.Badge;
import jenkins.model.Jenkins;
import org.jvnet.localizer.Localizable;
import org.kohsuke.accmod.Restricted;
@@ -214,5 +215,15 @@ public abstract class ManagementLink implements ExtensionPoint, Action {
}
}
/**
* A {@link Badge} shown as overlay over the icon on "Manage Jenkins".
*
* @return badge or {@code null} if no badge should be shown.
* @since TODO
*/
public @CheckForNull Badge getBadge() {
return null;
}
private static final Logger LOGGER = Logger.getLogger(ManagementLink.class.getName());
}
@@ -0,0 +1,104 @@
/*
* The MIT License
*
* Copyright (c) 2022, Markus Winter
*
* 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.management;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.AdministrativeMonitor;
import hudson.model.ManagementLink;
import java.util.Locale;
/**
* Definition of a badge that can be returned by a {@link ManagementLink} implementation.
* The badge is shown as a small overlay over the corresponding icon on the {@code Manage Jenkins} page,
* it can display additional information in a tooltip and change it's color depending on the severity.
*
* <p>
* A badge mainly serves as a fast feedback for the corresponding management page.
* It could be used to just display some short status information or hint that some action can be taken.
* For example the badge on {@code Manage Plugins} shows information about the number of available updates
* and in its tooltip additionally how many updates contain incompatible changes or fix
* security vulnerabilities. It also changes its color when there are security fixes available.
*
* <p>
* A badge might display the same information as an {@link AdministrativeMonitor}. While an {@link AdministrativeMonitor}
* can be disabled, a badge will always be shown. E.g. the badge of {@link OldDataMonitor.ManagementLinkImpl} always shows the number of old data entries.
*
* @since TODO
*/
public class Badge {
private final String text;
private final String tooltip;
private final Severity severity;
/**
* Create a new Badge
*
* @param text The text to be shown in the badge.
* Keep it short, ideally just a number. More than 6 or 7 characters do not look good. Avoid spaces as they will lead to line breaks.
* as this might lead to line breaks.
* @param tooltip The tooltip to show for the badge.
* Do not include html tags.
* @param severity The severity of the badge (danger, warning, info)
*/
public Badge(@NonNull String text, @NonNull String tooltip, @NonNull Severity severity) {
this.text = text;
this.tooltip = tooltip;
this.severity = severity;
}
/**
* The text to be shown in the badge.
*
* @return badge text
*/
public String getText() {
return text;
}
/**
* The tooltip of the badge.
*
* @return tooltip
*/
public String getTooltip() {
return tooltip;
}
/**
* The severity of the badge.
* Influences the background color of the badge.
*
* @return severity as String
*/
public String getSeverity() {
return severity.toString().toLowerCase(Locale.US);
}
public enum Severity {
DANGER, WARNING, INFO
}
}
@@ -28,11 +28,11 @@ import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.ManagementLink;
import hudson.model.UpdateCenter;
import hudson.model.UpdateSite.Plugin;
import hudson.security.Permission;
import java.util.List;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
@@ -72,13 +72,49 @@ public class PluginsLink extends ManagementLink {
return Category.CONFIGURATION;
}
@Restricted(NoExternalUse.class)
public int getUpdateCount() {
@Override
public Badge getBadge() {
final UpdateCenter updateCenter = Jenkins.get().getUpdateCenter();
if (!updateCenter.isSiteDataReady()) {
// Do not display message during this page load, but possibly later.
return 0;
return null;
}
return updateCenter.getUpdates().size();
List<Plugin> plugins = updateCenter.getUpdates();
int size = plugins.size();
if (size > 0) {
StringBuilder tooltip = new StringBuilder();
Badge.Severity severity = Badge.Severity.WARNING;
int securityFixSize = (int) plugins.stream().filter(plugin -> plugin.fixesSecurityVulnerabilities()).count();
int incompatibleSize = (int) plugins.stream().filter(plugin -> !plugin.isCompatibleWithInstalledVersion()).count();
if (size > 1) {
tooltip.append(Messages.PluginsLink_updatesAvailable(size));
} else {
tooltip.append(Messages.PluginsLink_updateAvailable());
}
switch (incompatibleSize) {
case 0:
break;
case 1:
tooltip.append("\n").append(Messages.PluginsLink_incompatibleUpdateAvailable());
break;
default:
tooltip.append("\n").append(Messages.PluginsLink_incompatibleUpdatesAvailable(incompatibleSize));
break;
}
switch (securityFixSize) {
case 0:
break;
case 1:
tooltip.append("\n").append(Messages.PluginsLink_securityUpdateAvailable());
severity = Badge.Severity.DANGER;
break;
default:
tooltip.append("\n").append(Messages.PluginsLink_securityUpdatesAvailable(securityFixSize));
severity = Badge.Severity.DANGER;
break;
}
return new Badge(Integer.toString(size), tooltip.toString(), severity);
}
return null;
}
}