diff --git a/src/main/java/org/jenkinsci/plugins/badge/actions/JobBadgeAction.java b/src/main/java/org/jenkinsci/plugins/badge/actions/JobBadgeAction.java index 4d91a37b2..00ed8e990 100644 --- a/src/main/java/org/jenkinsci/plugins/badge/actions/JobBadgeAction.java +++ b/src/main/java/org/jenkinsci/plugins/badge/actions/JobBadgeAction.java @@ -8,10 +8,14 @@ import hudson.model.Action; import hudson.model.Job; import hudson.model.Run; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import org.jenkins.ui.icon.IconSpec; import org.jenkinsci.plugins.badge.*; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.WebMethod; /** @@ -45,6 +49,31 @@ public String getUrlName() { return "badge"; } + public String getUrl() { + /* Needed for the jelly syntax hints page */ + String url = ""; + StaplerRequest req = Stapler.getCurrentRequest(); + if (req != null) { + url = req.getReferer(); + if (url == null) { + url = "null-referer"; + } + } + return url; + } + + public String getUrlEncodedFullName() { + /* Needed for the jelly syntax hints page */ + if (project == null) { + return "null-project-no-url-encoded-fullName"; + } + if (project.getFullName() == null) { + return "null-project-fullName-no-url-encoded-fullName"; + } + String fullName = URLEncoder.encode(project.getFullName(), StandardCharsets.UTF_8); + return fullName == null ? "null-url-encoded-fullName" : fullName; + } + @WebMethod(name = "icon") public HttpResponse doIcon( @QueryParameter String build, diff --git a/src/main/java/org/jenkinsci/plugins/badge/actions/RunBadgeAction.java b/src/main/java/org/jenkinsci/plugins/badge/actions/RunBadgeAction.java index be3232550..e633f0115 100644 --- a/src/main/java/org/jenkinsci/plugins/badge/actions/RunBadgeAction.java +++ b/src/main/java/org/jenkinsci/plugins/badge/actions/RunBadgeAction.java @@ -7,10 +7,13 @@ import hudson.model.Action; import hudson.model.Job; import hudson.model.Run; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import org.jenkins.ui.icon.IconSpec; import org.jenkinsci.plugins.badge.*; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.WebMethod; public class RunBadgeAction implements Action, IconSpec { @@ -42,6 +45,26 @@ public String getUrlName() { return "badge"; } + public String getUrl() { + /* TODO: Is a permission check needed here? */ + /* Needed for the jelly syntax hints page */ + String url = Stapler.getCurrentRequest().getReferer(); + return url == null ? "null-referer" : url; + } + + public String getUrlEncodedFullName() { + /* TODO: Is a permission check needed here? */ + /* Needed for the jelly syntax hints page */ + if (project == null) { + return "null-project-no-url-encoded-fullName"; + } + if (project.getFullName() == null) { + return "null-project-fullName-no-url-encoded-fullName"; + } + String fullName = URLEncoder.encode(project.getFullName(), StandardCharsets.UTF_8); + return fullName == null ? "null-url-encoded-fullName" : fullName; + } + @WebMethod(name = "icon") public HttpResponse doIcon( @QueryParameter String style, diff --git a/src/main/resources/org/jenkinsci/plugins/badge/actions/JobBadgeAction/index.groovy b/src/main/resources/org/jenkinsci/plugins/badge/actions/JobBadgeAction/index.groovy deleted file mode 100644 index 8cbe9a1c6..000000000 --- a/src/main/resources/org/jenkinsci/plugins/badge/actions/JobBadgeAction/index.groovy +++ /dev/null @@ -1,114 +0,0 @@ -package org.jenkinsci.plugins.badge.JobBadgeAction -import java.net.URLEncoder; - -def l = namespace(lib.LayoutTagLib) -def st = namespace("jelly:stapler") - -l.layout(type: "one-column") { - l.main_panel { - h2(_("Embeddable Build Status Icon")) - p(raw(_("blurb"))) - st.adjunct(includes: "org.jenkinsci.plugins.badge.actions.JobBadgeAction.ClickHandler") - link(rel: "stylesheet", href: "${rootURL}/plugin/embeddable-build-status/css/design.css", type: "text/css") - - def fullJobName = URLEncoder.encode(my.project.fullName, "UTF-8"); - def jobUrl = "${app.rootUrl}${my.project.url}"; - def badgeUrl = jobUrl + "badge/icon" - def textUrl = jobUrl + "badge/text" - def publicBadge = "${app.rootUrl}buildStatus/icon?job=${fullJobName}"; - def publicText = "${app.rootUrl}buildStatus/text?job=${fullJobName}"; - - h2(_("Examples")) - small(_("examples_note")) - - h3(_("flat")) - img(id:"badgeUrl", - src: badgeUrl, - title: badgeUrl) - raw ("
") - img(id:"badgeUrl", - src:badgeUrl + "?subject=Custom%20Subject&status=Any%20State&color=darkturquoise", - title:badgeUrl + "?subject=Custom%20Subject&status=Any%20State&color=darkturquoise") - - h3(_("flat-square: ")) - img(src:badgeUrl + "?style=flat-square", - title: badgeUrl + "?style=flat-square") - raw ("
") - img(src:badgeUrl + "?style=flat-square&subject=Custom%20Subject&status=Any%20State&color=darkturquoise", - title:badgeUrl + "?style=flat-square&subject=Custom%20Subject&status=Any%20State&color=darkturquoise") - - h3(_("plastic: ")) - img(src:badgeUrl + "?style=plastic", - title: badgeUrl + "?style=plastic") - raw ("
") - img(src:badgeUrl + "?style=plastic&subject=Custom%20Subject&status=Any%20State&color=darkturquoise", - title:badgeUrl + "?style=plastic&subject=Custom%20Subject&status=Any%20State&color=darkturquoise") - - h3(_("ball-: ")) - img(src:badgeUrl + "?style=ball-16x16", - title: badgeUrl + "?style=ball-16x16") - raw ("
") - img(src:badgeUrl + "?style=ball-32x32", - title: badgeUrl + "?style=ball-32x32") - - h2(_("Links")) - - h3(_("Plain Link")) - b {text(_("protected"))} - input(type:"text",value:badgeUrl,class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:publicBadge,class:"select-all") - - h3(_("Markdown")) - b {text(_("protected"))} - input(type:"text",value:"[![Build Status](${badgeUrl})](${jobUrl})",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[![Build Status](${publicBadge})](${jobUrl})",class:"select-all") - - h3(_("HTML")) - b {text(_("protected"))} - input(type:"text",value:"",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"",class:"select-all") - - h3(_("Confluence")) - b {text(_("protected"))} - input(type:"text",value:"[!${badgeUrl}!|${jobUrl}]",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[!${publicBadge}!|${jobUrl}]",class:"select-all") - - h3(_("XWiki")) - b {text(_("protected"))} - input(type:"text",value:"[[image:${badgeUrl}>>${jobUrl}||target='__new']]",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[[image:${publicBadge}>>${jobUrl}||target='__new']]",class:"select-all") - - h3(_("RDoc")) - b {text(_("protected"))} - input(type:"text",value:"{}[${jobUrl}]",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"{}[${jobUrl}]",class:"select-all") - - h3(_("Textile")) - b {text(_("protected"))} - input(type:"text",value:"\"!${badgeUrl}!\":${jobUrl}",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"\"!${publicBadge}!\":${jobUrl}",class:"select-all") - - h3(_("Bitbucket")) - b {text(_("protected"))} - input(type:"text",value:"[Build Status](${badgeUrl}) \"${jobUrl}\")",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[Build Status](${publicBadge} \"${jobUrl}\")",class:"select-all") - - h2(_("Embeddable Build Status Text")) - p(raw(_("blurb_text"))) - - h3(_("Text Only")) - b {text(_("protected"))} - input(type:"text",value:textUrl,class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:publicText,class:"select-all") - - } -} diff --git a/src/main/resources/org/jenkinsci/plugins/badge/actions/JobBadgeAction/index.jelly b/src/main/resources/org/jenkinsci/plugins/badge/actions/JobBadgeAction/index.jelly new file mode 100644 index 000000000..234c7812c --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/badge/actions/JobBadgeAction/index.jelly @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + +

${%Embeddable Build Status Icon}

+

${%blurb}

+ +

${%Examples}

+ ${%examples_note} + + + +

${%flat}

+ +
+ + + + + +

${%flat-square}

+ +
+ + + + + +

${%plastic}

+ +
+ + + + + +

${%ball-size}

+ +
+ + +

${%Links}

+ +

${%Plain link}

+

+ ${%Protected} +
+ ${badgeUrl} +

+

+ ${%Unprotected} +
+ ${publicBadge} +

+ + + + +

${%Markdown}

+

+ ${%Protected} +
+ ${markdownProtectedLink} +

+

+ ${%Unprotected} +
+ ${markdownUnprotectedLink} +

+ + + + +

${%HTML}

+

+ ${%Protected} +
+ ${htmlProtectedLink} +

+

+ ${%Unprotected} +
+ ${htmlUnprotectedLink} +

+ + + + +

${%Asciidoc}

+

+ ${%Protected} +
+ ${asciidocProtectedLink} +

+

+ ${%Unprotected} +
+ ${asciidocUnprotectedLink} +

+ + + + +

${%Confluence}

+

+ ${%Protected} +
+ ${confluenceProtectedLink} +

+

+ ${%Unprotected} +
+ ${confluenceUnprotectedLink} +

+ + + + +

${%XWiki}

+

+ ${%Protected} +
+ ${xwikiProtectedLink} +

+

+ ${%Unprotected} +
+ ${xwikiUnprotectedLink} +

+ + + + +

${%RDoc}

+

+ ${%Protected} +
+ ${rdocProtectedLink} +

+

+ ${%Unprotected} +
+ ${rdocUnprotectedLink} +

+ + + + +

${%Textile}

+

+ ${%Protected} +
+ ${textileProtectedLink} +

+

+ ${%Unprotected} +
+ ${textileUnprotectedLink} +

+ + + + +

${%Bitbucket}

+

+ ${%Protected} +
+ ${bitbucketProtectedLink} +

+

+ ${%Unprotected} +
+ ${bitbucketUnprotectedLink} +

+ +

${%Embeddable Build Status Text}

+

${%blurb_text}

+ +

${%Text Only}

+

+ ${%Protected} +
+ ${textUrl} +

+

+ ${%Unprotected} +
+ ${publicText} +

+ +
+
+
diff --git a/src/main/resources/org/jenkinsci/plugins/badge/actions/RunBadgeAction/index.groovy b/src/main/resources/org/jenkinsci/plugins/badge/actions/RunBadgeAction/index.groovy deleted file mode 100644 index 7f9f17870..000000000 --- a/src/main/resources/org/jenkinsci/plugins/badge/actions/RunBadgeAction/index.groovy +++ /dev/null @@ -1,113 +0,0 @@ -package org.jenkinsci.plugins.badge.RunBadgeAction -import java.net.URLEncoder; - -def l = namespace(lib.LayoutTagLib) -def st = namespace("jelly:stapler") - -l.layout(type: "one-column") { - l.main_panel { - h2(_("Embeddable Build Status Icon")) - p(raw(_("blurb"))) - st.adjunct(includes: "org.jenkinsci.plugins.badge.actions.JobBadgeAction.ClickHandler") - link(rel: "stylesheet", href: "${rootURL}/plugin/embeddable-build-status/css/design.css", type: "text/css") - - def fullJobName = URLEncoder.encode(my.project.fullName, "UTF-8"); - def jobUrl = "${app.rootUrl}${my.project.url}${my.run.number}/"; - def badgeUrl = jobUrl + "badge/icon" - def textUrl = jobUrl + "badge/text" - def publicBadge = "${app.rootUrl}buildStatus/icon?job=${fullJobName}&build=${my.run.number}"; - def publicText = "${app.rootUrl}buildStatus/text?job=${fullJobName}&build=${my.run.number}"; - - h2(_("Examples")) - small(_("examples_note")) - - h3(_("flat")) - img(id:"badgeUrl", - src: badgeUrl, - title: badgeUrl) - raw ("
") - img(id:"badgeUrl", - src:badgeUrl + "?subject=Custom%20Subject&status=Any%20State&color=darkturquoise", - title:badgeUrl + "?subject=Custom%20Subject&status=Any%20State&color=darkturquoise") - - h3(_("flat-square: ")) - img(src:badgeUrl + "?style=flat-square", - title: badgeUrl + "?style=flat-square") - raw ("
") - img(src:badgeUrl + "?style=flat-square&subject=Custom%20Subject&status=Any%20State&color=darkturquoise", - title:badgeUrl + "?style=flat-square&subject=Custom%20Subject&status=Any%20State&color=darkturquoise") - - h3(_("plastic: ")) - img(src:badgeUrl + "?style=plastic", - title: badgeUrl + "?style=plastic") - raw ("
") - img(src:badgeUrl + "?style=plastic&subject=Custom%20Subject&status=Any%20State&color=darkturquoise", - title:badgeUrl + "?style=plastic&subject=Custom%20Subject&status=Any%20State&color=darkturquoise") - - h3(_("ball-: ")) - img(src:badgeUrl + "?style=ball-16x16", - title: badgeUrl + "?style=ball-16x16") - raw ("
") - img(src:badgeUrl + "?style=ball-32x32", - title: badgeUrl + "?style=ball-32x32") - - h2(_("Links")) - - h3(_("Plain Link")) - b {text(_("protected"))} - input(type:"text",value:badgeUrl,class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:publicBadge,class:"select-all") - - h3(_("Markdown")) - b {text(_("protected"))} - input(type:"text",value:"[![Build Status](${badgeUrl})](${jobUrl})",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[![Build Status](${publicBadge})](${jobUrl})",class:"select-all") - - h3(_("HTML")) - b {text(_("protected"))} - input(type:"text",value:"",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"",class:"select-all") - - h3(_("Confluence")) - b {text(_("protected"))} - input(type:"text",value:"[!${badgeUrl}!|${jobUrl}]",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[!${publicBadge}!|${jobUrl}]",class:"select-all") - - h3(_("XWiki")) - b {text(_("protected"))} - input(type:"text",value:"[[image:${badgeUrl}>>${jobUrl}||target='__new']]",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[[image:${publicBadge}>>${jobUrl}||target='__new']]",class:"select-all") - - h3(_("RDoc")) - b {text(_("protected"))} - input(type:"text",value:"{}[${jobUrl}]",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"{}[${jobUrl}]",class:"select-all") - - h3(_("Textile")) - b {text(_("protected"))} - input(type:"text",value:"\"!${badgeUrl}!\":${jobUrl}",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"\"!${publicBadge}!\":${jobUrl}",class:"select-all") - - h3(_("Bitbucket")) - b {text(_("protected"))} - input(type:"text",value:"[Build Status](${badgeUrl}) \"${jobUrl}\")",class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:"[Build Status](${publicBadge} \"${jobUrl}\")",class:"select-all") - - h2(_("Embeddable Build Status Text")) - p(raw(_("blurb_text"))) - - h3(_("Text Only")) - b {text(_("protected"))} - input(type:"text",value:textUrl,class:"select-all") - b {text(_("unprotected"))} - input(type:"text",value:publicText,class:"select-all") - } -} diff --git a/src/main/resources/org/jenkinsci/plugins/badge/actions/RunBadgeAction/index.jelly b/src/main/resources/org/jenkinsci/plugins/badge/actions/RunBadgeAction/index.jelly new file mode 100644 index 000000000..2c0f43514 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/badge/actions/RunBadgeAction/index.jelly @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + +

${%Embeddable Build Status Icon}

+

${%blurb}

+ +

${%Examples}

+ ${%examples_note} + + + +

${%flat}

+ +
+ + + + + +

${%flat-square}

+ +
+ + + + + +

${%plastic}

+ +
+ + + + + +

${%ball-size}

+ +
+ + +

${%Links}

+ +

${%Plain link}

+

+ ${%Protected} +
+ ${badgeUrl} +

+

+ ${%Unprotected} +
+ ${publicBadge} +

+ + + + +

${%Markdown}

+

+ ${%Protected} +
+ ${markdownProtectedLink} +

+

+ ${%Unprotected} +
+ ${markdownUnprotectedLink} +

+ + + + +

${%HTML}

+

+ ${%Protected} +
+ ${htmlProtectedLink} +

+

+ ${%Unprotected} +
+ ${htmlUnprotectedLink} +

+ + + + +

${%Asciidoc}

+

+ ${%Protected} +
+ ${asciidocProtectedLink} +

+

+ ${%Unprotected} +
+ ${asciidocUnprotectedLink} +

+ + + + +

${%Confluence}

+

+ ${%Protected} +
+ ${confluenceProtectedLink} +

+

+ ${%Unprotected} +
+ ${confluenceUnprotectedLink} +

+ + + + +

${%XWiki}

+

+ ${%Protected} +
+ ${xwikiProtectedLink} +

+

+ ${%Unprotected} +
+ ${xwikiUnprotectedLink} +

+ + + + +

${%RDoc}

+

+ ${%Protected} +
+ ${rdocProtectedLink} +

+

+ ${%Unprotected} +
+ ${rdocUnprotectedLink} +

+ + + + +

${%Textile}

+

+ ${%Protected} +
+ ${textileProtectedLink} +

+

+ ${%Unprotected} +
+ ${textileUnprotectedLink} +

+ + + + +

${%Bitbucket}

+

+ ${%Protected} +
+ ${bitbucketProtectedLink} +

+

+ ${%Unprotected} +
+ ${bitbucketUnprotectedLink} +

+ +

${%Embeddable Build Status Text}

+

${%blurb_text}

+ +

${%Text Only}

+

+ ${%Protected} +
+ ${textUrl} +

+

+ ${%Unprotected} +
+ ${publicText} +

+ +
+
+
diff --git a/src/main/webapp/css/design.css b/src/main/webapp/css/design.css index 88cb27e30..66c23ee45 100644 --- a/src/main/webapp/css/design.css +++ b/src/main/webapp/css/design.css @@ -9,6 +9,12 @@ INPUT.select-all { IMG#badge { margin-left:2em; } -h3 { +h2 { + margin-bottom: 1px; + margin-top: 1em; +} +h3.help-format { border-bottom: 1px solid grey; + margin-bottom: 1px; + margin-top: 1em; } diff --git a/src/test/java/org/jenkinsci/plugins/badge/actions/JobBadgeActionTest.java b/src/test/java/org/jenkinsci/plugins/badge/actions/JobBadgeActionTest.java new file mode 100644 index 000000000..12d881335 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/badge/actions/JobBadgeActionTest.java @@ -0,0 +1,103 @@ +/* + * The MIT License + * + * Copyright 2023 Mark Waite. + * + * 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 org.jenkinsci.plugins.badge.actions; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import hudson.model.FreeStyleProject; +import hudson.model.Result; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class JobBadgeActionTest { + + @ClassRule + public static JenkinsRule j = new JenkinsRule(); + + private static final String NOT_BUILT_JOB_NAME = "not-built-job"; + private static JobBadgeAction notBuiltAction; + + private static final String SUCCESSFUL_JOB_NAME = "successful-job"; + private static JobBadgeAction successfulAction; + + public JobBadgeActionTest() {} + + @BeforeClass + public static void createAction() throws Exception { + /* Build a job for assertions on successful jobs */ + FreeStyleProject successfulJob = j.createFreeStyleProject(SUCCESSFUL_JOB_NAME); + j.buildAndAssertStatus(Result.SUCCESS, successfulJob); + successfulAction = new JobBadgeAction(successfulJob); + + /* Define a job and do not build it for assertions on jobs that have not been built */ + FreeStyleProject notBuiltJob = j.createFreeStyleProject(NOT_BUILT_JOB_NAME); + notBuiltAction = new JobBadgeAction(notBuiltJob); + } + + @Test + public void testGetIconFileName() { + assertThat(notBuiltAction.getIconFileName(), is(nullValue())); + assertThat(successfulAction.getIconFileName(), is(nullValue())); + } + + @Test + public void testGetIconClassName() { + assertThat(notBuiltAction.getIconClassName(), is("symbol-shield-outline plugin-ionicons-api")); + assertThat(successfulAction.getIconClassName(), is("symbol-shield-outline plugin-ionicons-api")); + } + + @Test + public void testGetDisplayName() { + assertThat(notBuiltAction.getDisplayName(), is("Embeddable Build Status")); + assertThat(successfulAction.getDisplayName(), is("Embeddable Build Status")); + } + + @Test + public void testGetUrlName() { + assertThat(notBuiltAction.getUrlName(), is("badge")); + assertThat(successfulAction.getUrlName(), is("badge")); + } + + @Test + public void testGetUrl() { + assertThat(notBuiltAction.getUrl(), is("")); + assertThat(successfulAction.getUrl(), is("")); + } + + @Test + public void testGetUrlEncodedFullName() { + assertThat(notBuiltAction.getUrlEncodedFullName(), is(NOT_BUILT_JOB_NAME)); + assertThat(successfulAction.getUrlEncodedFullName(), is(SUCCESSFUL_JOB_NAME)); + } + + @Test + public void testDoText() { + assertThat(notBuiltAction.doText(), is("Not built")); + assertThat(successfulAction.doText(), is("Success")); + } +}