Skip to content

Commit

Permalink
Merge pull request jenkinsci#260 from fcojfernandez/newNavBar
Browse files Browse the repository at this point in the history
[SDANAV-73][SDANAV-128][SDANAV-132] Allow override header from plugin
  • Loading branch information
Beatriz Muñoz committed Jan 14, 2021
2 parents 2cd2e5f + 10fdf41 commit 4b444b8
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 105 deletions.
24 changes: 24 additions & 0 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@
import jenkins.model.ModelObjectWithChildren;
import jenkins.model.ModelObjectWithContextMenu;

import jenkins.views.CbHeader;
import jenkins.views.OSSHeaderLayout;
import org.acegisecurity.AccessDeniedException;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
Expand Down Expand Up @@ -2297,4 +2299,26 @@ public static boolean isContextMenuVisible(Action a) {
return true;
}
}

// CloudBees proprietary functions
// TODO remove if we decide to rid of the former header completely
@Restricted(NoExternalUse.class)
public static OSSHeaderLayout ossHeader() {
return ExtensionList.lookupSingleton(OSSHeaderLayout.class);
}

@Restricted(NoExternalUse.class)
@CheckForNull
public static CbHeader honeyUIHeader() {
List<CbHeader> all = ExtensionList.lookup(CbHeader.class).stream().filter(cbHeader -> cbHeader.isCbHeaderEnabled()).collect(Collectors.toList());

if (all.size() > 0) {
if (all.size() > 1) {
LOGGER.warning("More than one configured header. This should not happen. Serving the first one and please review");
}
return all.get(0);
}

return null;
}
}
21 changes: 21 additions & 0 deletions core/src/main/java/jenkins/views/CbHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2020 CloudBees, Inc.
* This is proprietary code. All rights reserved.
*/
package jenkins.views;

import hudson.ExtensionPoint;

/**
* Abstract class to be extended from the SDA UI plugin to include the new header
*/
public class CbHeader implements ExtensionPoint {

/**
* Checks if CB header is enabled. By default it is if installed, but the logic is deferred in the plugins.
* @return
*/
public boolean isCbHeaderEnabled() {
return true;
}
}
17 changes: 17 additions & 0 deletions core/src/main/java/jenkins/views/OSSHeaderLayout.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2020 CloudBees, Inc.
* This is proprietary code. All rights reserved.
*/
package jenkins.views;

import hudson.Extension;
import hudson.ExtensionPoint;

/**
* Class to include the classic header in CloudBees products while the new SDA header lands
*
* TODO remove once the new plugins land in CAP
*/
@Extension
public class OSSHeaderLayout implements ExtensionPoint {
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ THE SOFTWARE.
<st:include page="/hudson/security/SecurityRealm/loginLink.jelly" />
<j:if test="${it.allowsSignup()}">
|
<a href="${rootURL}/signup"><b>${%sign up}</b></a>
<a data-testid="sign-up" href="${rootURL}/signup"><b>${%sign up}</b></a>
</j:if>
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ THE SOFTWARE.

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<a href="${rootURL}/${app.securityRealm.loginUrl}?from=${app.securityRealm.from}"><b>${%login}</b></a>
<a data-testid="log-in" href="${rootURL}/${app.securityRealm.loginUrl}?from=${app.securityRealm.from}"><b>${%login}</b></a>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!-- This jelly file will remain while we accept users to disable the new header -->
<?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.png" 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 data-testid="user-logged-in" 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 data-testid="logout" 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>
111 changes: 8 additions & 103 deletions core/src/main/resources/lib/layout/pageHeader.jelly
Original file line number Diff line number Diff line change
@@ -1,106 +1,11 @@
<?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.png" 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:choose>
<j:when test="${h.honeyUIHeader() != null}">
<st:include it="${h.honeyUIHeader()}" page="pageHeader.jelly"/>
</j:when>
<j:otherwise>
<st:include it="${h.ossHeader()}" page="ossPageHeader.jelly"/>
</j:otherwise>
</j:choose>
</j:jelly>

0 comments on commit 4b444b8

Please sign in to comment.