Skip to content

Commit

Permalink
Jenkins header revamp proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
imonteroperez committed Nov 10, 2021
1 parent cee0d06 commit f905a3f
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 103 deletions.
17 changes: 17 additions & 0 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@
import jenkins.model.ModelObjectWithContextMenu;
import jenkins.model.SimplePageDecorator;
import jenkins.util.SystemProperties;
import jenkins.views.Header;
import jenkins.views.JenkinsHeader;

import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
Expand Down Expand Up @@ -2282,4 +2285,18 @@ public static boolean isContextMenuVisible(Action a) {
return true;
}
}

@Restricted(NoExternalUse.class)
@CheckForNull
public static Header header() {
List<Header> headers = ExtensionList.lookup(Header.class).stream().filter(header -> header.isHeaderEnabled()).collect(Collectors.toList());
if (headers.size() > 0) {
if (headers.size() > 1) {
LOGGER.warning("More than one configured header. This should not happen. Serving the Jenkins default header and please review");
} else {
return headers.get(0);
}
}
return new JenkinsHeader();
}
}
13 changes: 13 additions & 0 deletions core/src/main/java/jenkins/views/Header.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package jenkins.views;

import hudson.ExtensionPoint;

public interface Header extends ExtensionPoint {

/**
* Checks if header is enabled. By default it is if installed, but the logic is deferred in the plugins.
* @return
*/
boolean isHeaderEnabled();

}
13 changes: 13 additions & 0 deletions core/src/main/java/jenkins/views/JenkinsHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package jenkins.views;

import hudson.Extension;

@Extension(ordinal = Integer.MIN_VALUE)
public class JenkinsHeader implements Header {

@Override
public boolean isHeaderEnabled() {
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:i="jelly:fmt" xmlns:x="jelly:xml">
<header id="header" class="page-header">
<div class="page-header__brand">
<div class="logo">
<a id="jenkins-home-link" href="${rootURL}/">
<img id="jenkins-head-icon" src="${imagesURL}/jenkins-header-logo-v2.svg" alt="[${logoAlt}]" />
<img id="jenkins-name-icon" src="${imagesURL}/title.svg" alt="${title}" width="139" height="34" />
</a>
</div>

<a class="page-header__brand-link" href="${rootURL}/">
<img src="${imagesURL}/jenkins-header-logo-v2.svg"
alt="[${logoAlt}]"
class="page-header__brand-image" />
<span class="page-header__brand-name">Jenkins</span>
</a>
</div>

<div class="searchbox hidden-xs">
<!-- search box -->
<j:set var="searchURL" value="${h.searchURL}"/>
<form action="${searchURL}" method="get" style="position:relative;" class="no-json" name="search" role="search">
<!-- this div is used to calculate the width of the text box -->
<div id="search-box-sizer"/>
<div id="searchform">
<input name="q" placeholder="${searchPlaceholder}" id="search-box" class="main-search__input" value="${request.getParameter('q')}" role="searchbox" />

<span class="main-search__icon-leading">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-action-symbol.svg#ic_search_24px" />
</span>
<a href="${searchHelpUrl}" class="main-search__icon-trailing">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-action-symbol.svg#ic_help_outline_24px" />
</a>

<div id="search-box-completion" />
<script>createSearchBox("${searchURL}");</script>
</div>
</form>
</div>

<div class="login page-header__hyperlinks">
<div id="visible-am-insertion" class="page-header__am-wrapper" />
<div id="visible-sec-am-insertion" class="page-header__am-wrapper" />

<!-- login field -->
<j:if test="${app.useSecurity}">
<j:choose>
<j:when test="${!h.isAnonymous()}">
<j:invokeStatic var="user" className="hudson.model.User" method="current" />
<j:choose>
<j:when test="${user.fullName == null || user.fullName.trim().isEmpty()}">
<j:set var="userName" value="${user.id}"/>
</j:when>
<j:otherwise>
<j:set var="userName" value="${user.fullName}"/>
</j:otherwise>
</j:choose>
<a href="${rootURL}/${user.url}" class="model-link inside inverse">
<l:svgIcon
class="am-monitor-icon" >
<use href="${resURL}/images/material-icons/svg-sprite-social-symbol.svg#ic_person_24px"></use>
</l:svgIcon>
<span class="hidden-xs hidden-sm">${userName}</span>
</a>
<j:if test="${app.securityRealm.canLogOut()}">
<a href="${rootURL}/logout">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-action-symbol.svg#ic_input_24px" />
<span class="hidden-xs hidden-sm">${logout}</span>
</a>
</j:if>
</j:when>
<j:otherwise>
<st:include it="${app.securityRealm}" page="loginLink.jelly" />
</j:otherwise>
</j:choose>
</j:if>
</div>
</header>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:i="jelly:fmt" xmlns:x="jelly:xml">
<st:documentation>
Generates the page header, along with its associated dynamic behaviours.
This tag is used by l:layout and not expected to be used by anyone else,
but it's written as separate tag for better readability of code.

<st:attribute name="title" required="true">
Page title and title attribute for the logo
</st:attribute>

<st:attribute name="logoAlt" required="true">
Alt text for the logo
</st:attribute>

<st:attribute name="searchPlaceholder" required="true">
Placeholder text for the search input
</st:attribute>

<st:attribute name="searchHelpUrl" required="true">
Link value for the help icon on the search box
</st:attribute>

<st:attribute name="logout" required="true">
Text for the logout link
</st:attribute>
</st:documentation>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:i="jelly:fmt" xmlns:x="jelly:xml">
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:i="jelly:fmt" xmlns:x="jelly:xml">
</j:jelly>
108 changes: 5 additions & 103 deletions core/src/main/resources/lib/layout/pageHeader.jelly
Original file line number Diff line number Diff line change
@@ -1,106 +1,8 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:i="jelly:fmt" xmlns:x="jelly:xml">
<st:documentation>
Generates the page header, along with its associated dynamic behaviours.
This tag is used by l:layout and not expected to be used by anyone else,
but it's written as separate tag for better readability of code.

<st:attribute name="title" required="true">
Page title and title attribute for the logo
</st:attribute>

<st:attribute name="logoAlt" required="true">
Alt text for the logo
</st:attribute>

<st:attribute name="searchPlaceholder" required="true">
Placeholder text for the search input
</st:attribute>

<st:attribute name="searchHelpUrl" required="true">
Link value for the help icon on the search box
</st:attribute>

<st:attribute name="logout" required="true">
Text for the logout link
</st:attribute>
</st:documentation>

<header id="header" class="page-header">
<div class="page-header__brand">
<div class="logo">
<a id="jenkins-home-link" href="${rootURL}/">
<img id="jenkins-head-icon" src="${imagesURL}/jenkins-header-logo-v2.svg" alt="[${logoAlt}]" />
<img id="jenkins-name-icon" src="${imagesURL}/title.svg" alt="${title}" width="139" height="34" />
</a>
</div>

<a class="page-header__brand-link" href="${rootURL}/">
<img src="${imagesURL}/jenkins-header-logo-v2.svg"
alt="[${logoAlt}]"
class="page-header__brand-image" />
<span class="page-header__brand-name">Jenkins</span>
</a>
</div>

<div class="searchbox hidden-xs">
<!-- search box -->
<j:set var="searchURL" value="${h.searchURL}"/>
<form action="${searchURL}" method="get" style="position:relative;" class="no-json" name="search" role="search">
<!-- this div is used to calculate the width of the text box -->
<div id="search-box-sizer"/>
<div id="searchform">
<input name="q" placeholder="${searchPlaceholder}" id="search-box" class="main-search__input" value="${request.getParameter('q')}" role="searchbox" />

<span class="main-search__icon-leading">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-action-symbol.svg#ic_search_24px" />
</span>
<a href="${searchHelpUrl}" class="main-search__icon-trailing">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-action-symbol.svg#ic_help_outline_24px" />
</a>

<div id="search-box-completion" />
<script>createSearchBox("${searchURL}");</script>
</div>
</form>
</div>

<div class="login page-header__hyperlinks">
<div id="visible-am-insertion" class="page-header__am-wrapper" />
<div id="visible-sec-am-insertion" class="page-header__am-wrapper" />

<!-- login field -->
<j:if test="${app.useSecurity}">
<j:choose>
<j:when test="${!h.isAnonymous()}">
<j:invokeStatic var="user" className="hudson.model.User" method="current" />
<j:choose>
<j:when test="${user.fullName == null || user.fullName.trim().isEmpty()}">
<j:set var="userName" value="${user.id}"/>
</j:when>
<j:otherwise>
<j:set var="userName" value="${user.fullName}"/>
</j:otherwise>
</j:choose>
<a href="${rootURL}/${user.url}" class="model-link inside inverse">
<l:svgIcon
class="am-monitor-icon" >
<use href="${resURL}/images/material-icons/svg-sprite-social-symbol.svg#ic_person_24px"></use>
</l:svgIcon>
<span class="hidden-xs hidden-sm">${userName}</span>
</a>
<j:if test="${app.securityRealm.canLogOut()}">
<a href="${rootURL}/logout">
<l:svgIcon href="${resURL}/images/material-icons/svg-sprite-action-symbol.svg#ic_input_24px" />
<span class="hidden-xs hidden-sm">${logout}</span>
</a>
</j:if>
</j:when>
<j:otherwise>
<st:include it="${app.securityRealm}" page="loginLink.jelly" />
</j:otherwise>
</j:choose>
</j:if>
</div>
</header>
<j:set var="header" value="${h.header()}" />
<st:include it="${header}" page="headerDocumentation.jelly"/>
<st:include it="${header}" page="preHeader.jelly"/>
<st:include it="${header}" page="headerContent.jelly"/>
<st:include it="${header}" page="postHeader.jelly"/>
</j:jelly>

0 comments on commit f905a3f

Please sign in to comment.