diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
index c6f7416c6e..b389221f76 100644
--- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
@@ -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;
+ }
}
}
diff --git a/core/src/main/java/hudson/model/AdministrativeMonitor.java b/core/src/main/java/hudson/model/AdministrativeMonitor.java
index 3c70207ff8..7a9a0dde97 100644
--- a/core/src/main/java/hudson/model/AdministrativeMonitor.java
+++ b/core/src/main/java/hudson/model/AdministrativeMonitor.java
@@ -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.
+ * 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.
*
*
*
diff --git a/core/src/main/java/hudson/model/ManagementLink.java b/core/src/main/java/hudson/model/ManagementLink.java
index 666c2fd125..86bfae783e 100644
--- a/core/src/main/java/hudson/model/ManagementLink.java
+++ b/core/src/main/java/hudson/model/ManagementLink.java
@@ -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());
}
diff --git a/core/src/main/java/jenkins/management/Badge.java b/core/src/main/java/jenkins/management/Badge.java
new file mode 100644
index 0000000000..ebae6f96e8
--- /dev/null
+++ b/core/src/main/java/jenkins/management/Badge.java
@@ -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.
+ *
+ *
+ * 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.
+ *
+ *
+ * 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
+ }
+}
diff --git a/core/src/main/java/jenkins/management/PluginsLink.java b/core/src/main/java/jenkins/management/PluginsLink.java
index 40fbd756e0..f935792752 100644
--- a/core/src/main/java/jenkins/management/PluginsLink.java
+++ b/core/src/main/java/jenkins/management/PluginsLink.java
@@ -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 Nicolas De Loof
@@ -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 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;
}
}
diff --git a/core/src/main/resources/hudson/diagnosis/Messages.properties b/core/src/main/resources/hudson/diagnosis/Messages.properties
index 11ff96dd7c..9ef785add8 100644
--- a/core/src/main/resources/hudson/diagnosis/Messages.properties
+++ b/core/src/main/resources/hudson/diagnosis/Messages.properties
@@ -2,6 +2,7 @@ MemoryUsageMonitor.USED=Used
MemoryUsageMonitor.TOTAL=Total
OldDataMonitor.Description=Scrub configuration files to remove remnants from old plugins and earlier versions.
OldDataMonitor.DisplayName=Manage Old Data
+OldDataMonitor.OldDataTooltip=Old data exists
HudsonHomeDiskUsageMonitor.DisplayName=Disk Usage Monitor
NullIdDescriptorMonitor.DisplayName=Missing Descriptor ID
diff --git a/core/src/main/resources/hudson/diagnosis/Messages_de.properties b/core/src/main/resources/hudson/diagnosis/Messages_de.properties
index 8e7ea3d41e..26cb82440b 100644
--- a/core/src/main/resources/hudson/diagnosis/Messages_de.properties
+++ b/core/src/main/resources/hudson/diagnosis/Messages_de.properties
@@ -24,6 +24,7 @@ MemoryUsageMonitor.USED=Verwendet
MemoryUsageMonitor.TOTAL=Total
OldDataMonitor.DisplayName=Veraltete Daten verwalten
OldDataMonitor.Description=Konfiguration bereinigen, um Überbleibsel alter Plugins und früherer Versionen zu entfernen.
+OldDataMonitor.OldDataTooltip=Es existieren veraltete Daten
HudsonHomeDiskUsageMonitor.DisplayName=Speicherplatz-Monitor
NullIdDescriptorMonitor.DisplayName=Fehlende Descriptor-ID
ReverseProxySetupMonitor.DisplayName=Reverse-Proxy-Konfiguration
diff --git a/core/src/main/resources/hudson/model/ManageJenkinsAction/index.jelly b/core/src/main/resources/hudson/model/ManageJenkinsAction/index.jelly
index 74ebf219de..27be7d16d2 100644
--- a/core/src/main/resources/hudson/model/ManageJenkinsAction/index.jelly
+++ b/core/src/main/resources/hudson/model/ManageJenkinsAction/index.jelly
@@ -89,9 +89,10 @@ THE SOFTWARE.
-
-
- ${m.getUpdateCount()}
+
+
+
+ ${badge.text}
diff --git a/core/src/main/resources/hudson/model/ManageJenkinsAction/index.properties b/core/src/main/resources/hudson/model/ManageJenkinsAction/index.properties
index 774466c6ef..ffd34b8300 100644
--- a/core/src/main/resources/hudson/model/ManageJenkinsAction/index.properties
+++ b/core/src/main/resources/hudson/model/ManageJenkinsAction/index.properties
@@ -21,5 +21,3 @@
# THE SOFTWARE.
are.you.sure={0}: are you sure?
-updateAvailable={0} update available
-updatesAvailable={0} updates available
\ No newline at end of file
diff --git a/core/src/main/resources/hudson/model/ManageJenkinsAction/index_pt_BR.properties b/core/src/main/resources/hudson/model/ManageJenkinsAction/index_pt_BR.properties
index 4ede2f490c..e264c8e7a0 100644
--- a/core/src/main/resources/hudson/model/ManageJenkinsAction/index_pt_BR.properties
+++ b/core/src/main/resources/hudson/model/ManageJenkinsAction/index_pt_BR.properties
@@ -22,4 +22,3 @@
Manage\ Jenkins=Gerenciar o Jenkins
are.you.sure={0}: tem certeza?
-updateAvailable={0} atualização(ões) disponível(is)
diff --git a/core/src/main/resources/hudson/model/ManageJenkinsAction/index_zh_TW.properties b/core/src/main/resources/hudson/model/ManageJenkinsAction/index_zh_TW.properties
index f2ff89ae36..14e9fb1415 100644
--- a/core/src/main/resources/hudson/model/ManageJenkinsAction/index_zh_TW.properties
+++ b/core/src/main/resources/hudson/model/ManageJenkinsAction/index_zh_TW.properties
@@ -21,8 +21,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
are.you.sure={0}\: 您確定嗎?
-updateAvailable={0} 有更新
-updatesAvailable={0} 有更新
Manage\ Jenkins=管理 Jenkins
Configure\ System=設定系統
Configure\ global\ settings\ and\ paths.=設定全域設定值及路徑。
diff --git a/core/src/main/resources/jenkins/management/Messages.properties b/core/src/main/resources/jenkins/management/Messages.properties
index 55fca7d7a1..6c2990170b 100644
--- a/core/src/main/resources/jenkins/management/Messages.properties
+++ b/core/src/main/resources/jenkins/management/Messages.properties
@@ -34,6 +34,12 @@ ReloadLink.Description=Discard all the loaded data in memory and reload everythi
PluginsLink.DisplayName=Manage Plugins
PluginsLink.Description=Add, remove, disable or enable plugins that can extend the functionality of Jenkins.
+PluginsLink.updateAvailable=1 update available
+PluginsLink.updatesAvailable={0} updates available
+PluginsLink.securityUpdateAvailable=1 update fixes security vulnerabilities
+PluginsLink.securityUpdatesAvailable={0} updates fix security vulnerabilities
+PluginsLink.incompatibleUpdateAvailable=1 update is incompatible with the installed version
+PluginsLink.incompatibleUpdatesAvailable={0} updates are incompatible with the installed version
SystemInfoLink.DisplayName=System Information
SystemInfoLink.Description=Displays various environmental information to assist trouble-shooting.
diff --git a/core/src/main/resources/jenkins/management/Messages_de.properties b/core/src/main/resources/jenkins/management/Messages_de.properties
index d4f58db261..d90a69e53c 100644
--- a/core/src/main/resources/jenkins/management/Messages_de.properties
+++ b/core/src/main/resources/jenkins/management/Messages_de.properties
@@ -26,6 +26,13 @@ ReloadLink.DisplayName=Konfiguration von Festplatte neu laden
ReloadLink.Description=Verwirft alle Daten im Speicher und lädt die Konfigurationsdateien erneut aus dem Dateisystem.\n\
Dies ist nützlich, wenn Sie Konfigurationsdateien direkt editiert haben.
PluginsLink.DisplayName=Plugins verwalten
+PluginsLink.updateAvailable=1 Update verfügbar
+PluginsLink.updatesAvailable={0} Updates verfügbar
+PluginsLink.securityUpdateAvailable=1 Update behebt Sicherheitslücken
+PluginsLink.securityUpdatesAvailable={0} Updates beheben Sicherheitslücken
+PluginsLink.incompatibleUpdateAvailable=1 Update ist inkompatibel mit der installierten Version
+PluginsLink.incompatibleUpdatesAvailable={0} Updates sind inkompatibel mit der installierten Version
+
SystemInfoLink.DisplayName=Systeminformationen
SystemInfoLink.Description=Zeigt Umgebungsinformationen an, z.B. zur Unterstützung bei der Fehlersuche.
SystemLogLink.Description=Zeigt protokollierte Ereignisse im Jenkins Systemlog an.
@@ -41,6 +48,7 @@ StatisticsLink.DisplayName=Nutzungsstatistiken
StatisticsLink.Description=Ressourcenauslastung überwachen und überprüfen, ob weitere Build-Knoten sinnvoll wären.
NodesLink.DisplayName=Knoten verwalten
NodesLink.Description=Knoten hinzufügen, entfernen, steuern und überwachen, auf denen Jenkins Projekte verteilt ausführen kann.
+
CliLink.Description=Jenkins aus der Kommandozeile oder skriptgesteuert nutzen und verwalten.
CliLink.DisplayName=Jenkins CLI
SystemLogLink.DisplayName=Systemlog
diff --git a/core/src/main/resources/jenkins/management/Messages_pt_BR.properties b/core/src/main/resources/jenkins/management/Messages_pt_BR.properties
index 51df8be5f6..cacd0deb00 100644
--- a/core/src/main/resources/jenkins/management/Messages_pt_BR.properties
+++ b/core/src/main/resources/jenkins/management/Messages_pt_BR.properties
@@ -31,6 +31,8 @@ ConfigureLink.Description=Configurar opções globais e caminhos
ReloadLink.Description=Descartar todos os dados carregados na memória e recarregar tudo do sistema de arquivos. \
Isso é útil quando seus arquivos de configuração foram modificados diretamente no disco.
PluginsLink.DisplayName=Gerenciar extensões
+PluginsLink.updateAvailable=1 atualização disponível
+PluginsLink.updatesAvailable={0} atualizaçãões disponíveis
SystemInfoLink.DisplayName=Informações do sistema
SystemInfoLink.Description=Mostrar várias informações do ambiente para auxiliar na resolução de problemas.
SystemLogLink.DisplayName=Registro de atividades do sistema
diff --git a/core/src/main/resources/jenkins/management/Messages_zh_TW.properties b/core/src/main/resources/jenkins/management/Messages_zh_TW.properties
index 3cf80d7c07..11271acd5b 100644
--- a/core/src/main/resources/jenkins/management/Messages_zh_TW.properties
+++ b/core/src/main/resources/jenkins/management/Messages_zh_TW.properties
@@ -34,6 +34,8 @@ ReloadLink.Description=放棄所有在記憶體裡的資料,全部由檔案系
PluginsLink.DisplayName=管理外掛程式
PluginsLink.Description=外掛程式是能擴充 Jenkins 功能的程式,您可以在這裡新增、移除、停用或是啟用它們。
+PluginsLink.updateAvailable=1 有更新
+PluginsLink.updatesAvailable={0} 有更新
SystemInfoLink.DisplayName=系統資訊
SystemInfoLink.Description=顯示系統環境資訊,協助問題處理。
diff --git a/war/src/main/less/modules/section.less b/war/src/main/less/modules/section.less
index 30021bce3a..822a0ccc38 100644
--- a/war/src/main/less/modules/section.less
+++ b/war/src/main/less/modules/section.less
@@ -103,14 +103,12 @@
display: flex;
align-items: center;
justify-content: center;
- background: #ff0b27;
border-radius: 100px;
font-size: 0.7rem;
min-height: 20px;
min-width: 20px;
padding: 0 0.4rem;
box-shadow: 0 1px 1px rgba(black, 0.1);
- color: white;
animation: animate-in-badge var(--elastic-transition) 0.1s both;
&::after {