diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index d416b61095..9a0b27ce90 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -47,6 +47,7 @@ import hudson.model.JobPropertyDescriptor; import hudson.model.ModelObject; import hudson.model.Node; import hudson.model.PageDecorator; +import jenkins.model.SimplePageDecorator; import hudson.model.PaneStatusProperties; import hudson.model.ParameterDefinition; import hudson.model.ParameterDefinition.ParameterDescriptor; @@ -133,8 +134,6 @@ import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu; -import org.acegisecurity.Authentication; -import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.apache.commons.jelly.JellyContext; import org.apache.commons.jelly.JellyTagException; import org.apache.commons.jelly.Script; @@ -1772,7 +1771,14 @@ public class Functions { if(Jenkins.getInstanceOrNull()==null) return Collections.emptyList(); return PageDecorator.all(); } - + /** + * Gets only one {@link SimplePageDecorator}. + * @since FIXME + */ + public static SimplePageDecorator getSimplePageDecorator() { + return SimplePageDecorator.first(); + } + public static List> getCloudDescriptors() { return Cloud.all(); } diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index 389517c577..cf6c94e085 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -78,11 +78,7 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import java.io.IOException; import java.lang.reflect.Constructor; import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.MissingResourceException; -import java.util.ResourceBundle; +import java.util.*; import java.util.logging.Logger; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -312,13 +308,22 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea public User createAccountFromSetupWizard(StaplerRequest req) throws IOException, AccountCreationFailedException { checkPermission(Jenkins.ADMINISTER); SignupInfo si = validateAccountCreationForm(req, false); - if (si.errorMessage != null) { - throw new AccountCreationFailedException(si.errorMessage); + if (!si.errors.isEmpty()) { + String messages = getErrorMessages(si); + throw new AccountCreationFailedException(messages); } else { return createAccount(si); } } + private String getErrorMessages(SignupInfo si) { + StringBuilder messages = new StringBuilder(); + for (String message : si.errors.values()) { + messages.append(message).append(" | "); + } + return messages.toString(); + } + /** * Creates a first admin user account. * @@ -363,7 +368,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea private User createAccount(StaplerRequest req, StaplerResponse rsp, boolean validateCaptcha, String formView) throws ServletException, IOException { SignupInfo si = validateAccountCreationForm(req, validateCaptcha); - if (si.errorMessage != null) { + if (!si.errors.isEmpty()) { // failed. ask the user to try again. req.getView(this, formView).forward(req, rsp); return null; @@ -377,7 +382,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea * @param validateCaptcha whether to attempt to validate a captcha in the request * * @return a {@link SignupInfo#SignupInfo(StaplerRequest) SignupInfo from given request}, with {@link - * SignupInfo#errorMessage} set to a non-null value if any of the supported fields are invalid + * SignupInfo#errors} set to a non-null value if any of the supported fields are invalid */ private SignupInfo validateAccountCreationForm(StaplerRequest req, boolean validateCaptcha) { // form field validation @@ -385,24 +390,16 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea SignupInfo si = new SignupInfo(req); if (validateCaptcha && !validateCaptcha(si.captcha)) { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_TextNotMatchWordInImage(); - } - - if (si.password1 != null && !si.password1.equals(si.password2)) { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordNotMatch(); - } - - if (!(si.password1 != null && si.password1.length() != 0)) { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordRequired(); + si.errors.put("captcha", Messages.HudsonPrivateSecurityRealm_CreateAccount_TextNotMatchWordInImage()); } if (si.username == null || si.username.length() == 0) { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameRequired(); + si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameRequired()); } else if(!containsOnlyAcceptableCharacters(si.username)) { if (ID_REGEX == null) { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameInvalidCharacters(); + si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameInvalidCharacters()); } else { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameInvalidCharactersCustom(ID_REGEX); + si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameInvalidCharactersCustom(ID_REGEX)); } } else { // do not create the user - we just want to check if the user already exists but is not a "login" user. @@ -410,7 +407,15 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea if (null != user) // Allow sign up. SCM people has no such property. if (user.getProperty(Details.class) != null) - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameAlreadyTaken(); + si.errors.put("username", Messages.HudsonPrivateSecurityRealm_CreateAccount_UserNameAlreadyTaken()); + } + + if (si.password1 != null && !si.password1.equals(si.password2)) { + si.errors.put("password1", Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordNotMatch()); + } + + if (!(si.password1 != null && si.password1.length() != 0)) { + si.errors.put("password1", Messages.HudsonPrivateSecurityRealm_CreateAccount_PasswordRequired()); } if (si.fullname == null || si.fullname.length() == 0) { @@ -418,31 +423,32 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea } if (isMailerPluginPresent() && (si.email == null || !si.email.contains("@"))) { - si.errorMessage = Messages.HudsonPrivateSecurityRealm_CreateAccount_InvalidEmailAddress(); + si.errors.put("email", Messages.HudsonPrivateSecurityRealm_CreateAccount_InvalidEmailAddress()); } if (!User.isIdOrFullnameAllowed(si.username)) { - si.errorMessage = hudson.model.Messages.User_IllegalUsername(si.username); + si.errors.put("username", hudson.model.Messages.User_IllegalUsername(si.username)); } if (!User.isIdOrFullnameAllowed(si.fullname)) { - si.errorMessage = hudson.model.Messages.User_IllegalFullname(si.fullname); + si.errors.put("fullname", hudson.model.Messages.User_IllegalFullname(si.fullname)); } req.setAttribute("data", si); // for error messages in the view return si; } /** - * Creates a new account from a valid signup info. A signup info is valid if its {@link SignupInfo#errorMessage} - * field is null. + * Creates a new account from a valid signup info. A signup info is valid if its {@link SignupInfo#errors} + * field is empty. * * @param si the valid signup info to create an account from * @return a valid {@link User} object created from given signup info * @throws IllegalArgumentException if an invalid signup info is passed */ private User createAccount(SignupInfo si) throws IOException { - if (si.errorMessage != null) { - throw new IllegalArgumentException("invalid signup info passed to createAccount(si): " + si.errorMessage); + if (!si.errors.isEmpty()) { + String messages = getErrorMessages(si); + throw new IllegalArgumentException("invalid signup info passed to createAccount(si): " + messages); } // register the user User user = createAccount(si.username, si.password1); @@ -541,6 +547,8 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea */ public String errorMessage; + public HashMap errors = new HashMap(); + public SignupInfo() { } diff --git a/core/src/main/java/jenkins/model/DefaultSimplePageDecorator.java b/core/src/main/java/jenkins/model/DefaultSimplePageDecorator.java new file mode 100644 index 0000000000..6a4a9278a3 --- /dev/null +++ b/core/src/main/java/jenkins/model/DefaultSimplePageDecorator.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * + * Copyright 2018, CloudBees, Inc. + * + * 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.model; + +import hudson.Extension; + +/** + * In case there are no other implementations we will fallback to this implementation. + * + * To make sure that we load this extension last (or at least very late) we use a negative ordinal. + * This allows custom implementation to be "first" + */ +@Extension(ordinal=-9999) +public class DefaultSimplePageDecorator extends SimplePageDecorator { +} diff --git a/core/src/main/java/jenkins/model/SimplePageDecorator.java b/core/src/main/java/jenkins/model/SimplePageDecorator.java new file mode 100644 index 0000000000..866fc69925 --- /dev/null +++ b/core/src/main/java/jenkins/model/SimplePageDecorator.java @@ -0,0 +1,72 @@ +/* + * The MIT License + * + * Copyright 2018, CloudBees, Inc. + * + * 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.model; + +import hudson.DescriptorExtensionList; +import hudson.ExtensionPoint; +import hudson.model.Describable; +import hudson.model.Descriptor; +/** + * Participates in the rendering of the login page + * + *

+ * This class provides a few hooks to augment the HTML of the login page. + * + * @since TODO + */ +public class SimplePageDecorator extends Descriptor implements ExtensionPoint, Describable { + + protected SimplePageDecorator() { + super(self()); + } + + @Override + public final Descriptor getDescriptor() { + return this; + } + /** + * Obtains the URL of this object, excluding the context path. + * + *

+ * Every {@link SimplePageDecorator} is bound to URL via {@link Jenkins#getDescriptor()}. + * This method returns such an URL. + */ + public final String getUrl() { + return "descriptor/"+clazz.getName(); + } + + /** + * The first found LoginDecarator, there can only be one. + * @return the first found {@link SimplePageDecorator} + */ + public static SimplePageDecorator first(){ + DescriptorExtensionList descriptorList = Jenkins.getInstanceOrNull().getDescriptorList(SimplePageDecorator.class); + if (descriptorList.size() >= 1) { + return descriptorList.get(0); + } else { + return null; + } + } + +} diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly index f2477da1df..6312f5b8b4 100644 --- a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/signup.jelly @@ -26,6 +26,275 @@ THE SOFTWARE. User self sign up page. --> - - + + + + + + + + + + + ${h.initPageVariables(context)} + + + + + + + ${%Create an account! [Jenkins]} + + + + + + + +

+ +
+ + diff --git a/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly b/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly index 625be9422f..75157ee49a 100644 --- a/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly +++ b/core/src/main/resources/hudson/util/HudsonIsLoading/index.jelly @@ -23,22 +23,41 @@ THE SOFTWARE. --> - - - - - - - - -

- ${%Please wait while Jenkins is getting ready to work}... -

-

- ${%Your browser will reload automatically when Jenkins is ready.} -

- -
-
+ + + + + + + + + + + ${%Starting Jenkins} + + + + + + + +
+ +
+ + + +
\ No newline at end of file diff --git a/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly b/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly index 65e8bb6e45..aafd2cfc60 100644 --- a/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly +++ b/core/src/main/resources/hudson/util/HudsonIsRestarting/index.jelly @@ -23,22 +23,41 @@ THE SOFTWARE. --> - - - - - - - - -

- ${%Please wait while Jenkins is restarting}... -

-

- ${%Your browser will reload automatically when Jenkins is ready.} -

- -
-
-
\ No newline at end of file + + + + + + + + + + + ${%Restarting Jenkins} + + + + + + + +
+ +
+ + + + +
diff --git a/core/src/main/resources/jenkins/model/DefaultSimplePageDecorator/simple-head.jelly b/core/src/main/resources/jenkins/model/DefaultSimplePageDecorator/simple-head.jelly new file mode 100644 index 0000000000..c8ff1fb711 --- /dev/null +++ b/core/src/main/resources/jenkins/model/DefaultSimplePageDecorator/simple-head.jelly @@ -0,0 +1,29 @@ + + + + + + + diff --git a/core/src/main/resources/jenkins/model/DefaultSimplePageDecorator/simple-header.jelly b/core/src/main/resources/jenkins/model/DefaultSimplePageDecorator/simple-header.jelly new file mode 100644 index 0000000000..59c61a3c2c --- /dev/null +++ b/core/src/main/resources/jenkins/model/DefaultSimplePageDecorator/simple-header.jelly @@ -0,0 +1,30 @@ + + + +
+ +

${%Welcome to Jenkins!}

+
+
diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.jelly b/core/src/main/resources/jenkins/model/Jenkins/login.jelly index cb4ed08319..3276493af8 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/login.jelly @@ -23,58 +23,136 @@ THE SOFTWARE. --> - - - - - - - - - - - -
- -
- - - - - - - - - - - - - - - - -
${%User}:
${%Password}:
- - - - + + + + + + + + ${h.advertiseHeaders(response)} + + + + + + + + + + ${h.initPageVariables(context)} + + + + + + + + + + ${%signIn} [Jenkins] + + + + + + + + + + + +
+ - -
- - -
-
+ + + +
diff --git a/core/src/main/resources/jenkins/model/Jenkins/login.properties b/core/src/main/resources/jenkins/model/Jenkins/login.properties index d297fd6dc9..1cb864cfbb 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/login.properties +++ b/core/src/main/resources/jenkins/model/Jenkins/login.properties @@ -20,5 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -signUp=Create an account if you are not a member yet. +signUp=Please sign in below or create an account. login=log in +signIn=Sign in +loginWelcome=Welcome to Jenkins! \ No newline at end of file diff --git a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly index a5a9549aca..0e178b2df8 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/loginError.jelly @@ -24,7 +24,7 @@ THE SOFTWARE. - + @@ -32,28 +32,9 @@ THE SOFTWARE. - - - - - - -
- ${%Invalid login information. Please try again.} -
- ${%Try again} -
-
-
- ${%If you are a system administrator and suspect this to be a configuration problem, see the server console output for more details.} -
-
-
-
+ +
diff --git a/war/src/main/webapp/css/loading.css b/war/src/main/webapp/css/loading.css new file mode 100644 index 0000000000..c6ed611c28 --- /dev/null +++ b/war/src/main/webapp/css/loading.css @@ -0,0 +1,86 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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. + */ +@keyframes blink { + /** + * At the start of the animation the dot + * has an opacity of .2 + */ + 0% { + opacity: .2; + } + /** + * At 20% the dot is fully visible and + * then fades out slowly + */ + 20% { + opacity: 1; + } + /** + * Until it reaches an opacity of .2 and + * the animation can start again + */ + 100% { + opacity: .2; + } +} +.loading span { + /** + * Use the blink animation, which is defined above + */ + animation-name: blink; + /** + * The animation should take 1.4 seconds + */ + animation-duration: 1.4s; + /** + * It will repeat itself forever + */ + animation-iteration-count: infinite; + /** + * This makes sure that the starting style (opacity: .2) + * of the animation is applied before the animation starts. + * Otherwise we would see a short flash or would have + * to set the default styling of the dots to the same + * as the animation. Same applies for the ending styles. + */ + animation-fill-mode: both; +} + +.loading span:nth-child(2) { + /** + * Starts the animation of the third dot + * with a delay of .2s, otherwise all dots + * would animate at the same time + */ + animation-delay: .2s; +} + +.loading span:nth-child(3) { + /** + * Starts the animation of the third dot + * with a delay of .4s, otherwise all dots + * would animate at the same time + */ + animation-delay: .4s; +} diff --git a/war/src/main/webapp/css/signup.css b/war/src/main/webapp/css/signup.css new file mode 100644 index 0000000000..17a7b58a38 --- /dev/null +++ b/war/src/main/webapp/css/signup.css @@ -0,0 +1,56 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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. + */ +.signup{ + min-width: 480px; + max-width: 480px; + width: 480px; +} +.signupIntroDefault { + text-align: center; +} +form.signup{ + min-width: 480px; + max-width: 480px; + width: 480px; + text-align: left; +} +.inputHeader { + display: flex; + justify-content: space-between; + align-items: center; +} +#showPassword { + width: unset; + margin-right: 5px; +} +.combinedElement{ + display: flex; + align-items: center; +} +label { + margin-bottom: 7px; +} +#passwordStrengthWrapper { + margin-top: 4px; +} \ No newline at end of file diff --git a/war/src/main/webapp/css/simple-page-forms.css b/war/src/main/webapp/css/simple-page-forms.css new file mode 100644 index 0000000000..cac4a03afd --- /dev/null +++ b/war/src/main/webapp/css/simple-page-forms.css @@ -0,0 +1,79 @@ +.Checkbox { + display: inline-block; +} +.Checkbox .svg-icon { + width: 20px; + height: 20px; +} +.Checkbox .Checkbox-wrapper { + display: flex; + align-items: center; +} +.Checkbox input[type="checkbox"] { + border: 0; + clip: rect(0, 0, 0, 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.Checkbox .Checkbox-indicator { + display: flex; + border: 1px solid rgba(73, 73, 73, 0.5); + border-radius: 3px; + cursor: pointer; +} +.Checkbox .Checkbox-text { + margin-left: 5px; + cursor: pointer; +} +.Checkbox .svg-icon { + opacity: 0; + fill: #fff; + transition: opacity 0.1s linear; +} +.Checkbox input[type="checkbox"]:checked + .Checkbox-indicator { + background-color: #4A90E2; + border-color: #4A90E2; +} +.Checkbox input[type="checkbox"]:checked + .Checkbox-indicator .svg-icon { + opacity: 1; +} +.Checkbox input[type="checkbox"]:focus + .Checkbox-indicator, +.Checkbox.focus .Checkbox-indicator { + outline: 0; + box-shadow: inset 0 0 2px 3px rgba(0, 0, 0, 0.2); +} +.Checkbox input[disabled] + .Checkbox-indicator, +fieldset[disabled] .Checkbox input + .Checkbox-indicator, +.Checkbox input[disabled] ~ .Checkbox-text, +fieldset[disabled] .Checkbox input ~ .Checkbox-text { + opacity: 0.4; + cursor: auto; +} +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .Checkbox input[type="checkbox"]:focus + .Checkbox-indicator, + .Checkbox.focus .Checkbox-indicator { + box-shadow: inset 0 0 1px 3px rgba(0, 0, 0, 0.2); + } +} +@supports (-ms-ime-align:auto) { + .Checkbox input[type="checkbox"]:focus + .Checkbox-indicator, + .Checkbox.focus .Checkbox-indicator { + box-shadow: inset 0 0 1px 3px rgba(0, 0, 0, 0.2); + } +} +.Checkbox-small .svg-icon { + width: 16px; + height: 16px; +} +.Checkbox-medium .svg-icon { + width: 20px; + height: 20px; +} +.Checkbox-large .svg-icon { + width: 24px; + height: 24px; +} \ No newline at end of file diff --git a/war/src/main/webapp/css/simple-page.css b/war/src/main/webapp/css/simple-page.css new file mode 100644 index 0000000000..d269ceb791 --- /dev/null +++ b/war/src/main/webapp/css/simple-page.css @@ -0,0 +1,135 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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. + */ +html { + height: 100vh; + font-family: "LatoLatinWeb", "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 18px; +} + +.simple-page h1 { + font-size: 24px; +} + +.simple-page { + text-align: center; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-box-align: center; + -moz-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; + -webkit-box-pack: center; + -moz-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; + min-height: 90vh; +} + +.simple-page form { + min-width: 300px; + max-width: 300px; + width: 300px; +} + +.simple-page input { + padding: 6px 8px; + display: inline-block; + text-align: left; + text-decoration: none; + font-size: 18px; + border-radius: 3px; + border: 1px solid rgba(74,74,74,0.25); +} + +.simple-page input[type=text], +.simple-page input[type=password], +.simple-page input[type=submit] { + width: 100%; + box-sizing: border-box; +} + +.simple-page .formRow { + margin-top: 27px; +} +.simple-page .formRow ~ .formRow { + margin-top: 16px; +} + +.simple-page input[type=submit] { + text-align: center; +} + +.simple-page form .submit input { + border-radius: 3px; + color: #fff; + cursor: pointer; +} + +.simple-page .alert { + text-align: center; + font-size: 14px; + line-height: 18px; +} + +.simple-page .Checkbox-wrapper { + justify-content: center; +} +.simple-page .login .Checkbox-wrapper { + margin-top: 20px; +} +#remember_me{ + width: unset; + margin-right: 5px; +} + +/* forms */ + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +.layout-small { + font-size: 12px; +} + +.layout-medium { + font-size: 14px; +} + +.layout-large { + font-size: 18px; +} \ No newline at end of file diff --git a/war/src/main/webapp/css/simple-page.theme.css b/war/src/main/webapp/css/simple-page.theme.css new file mode 100644 index 0000000000..af00024ceb --- /dev/null +++ b/war/src/main/webapp/css/simple-page.theme.css @@ -0,0 +1,66 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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. + */ +/* + * IE11 prevents of the usage of variable, + * but to show the color theme we left it here + */ +:root { + --main-bg-color-focus: #2171ce; + --main-bg-color: #4A90E2; + --danger-color: #C4000A; +} + +html { + color: #4A4A4A; +} + +.simple-page .logo { + background-image: url('../images/jenkins.svg'); + background-repeat: no-repeat; + background-position: 50% 0; + height: 130px; +} + +.simple-page form .submit input:focus { + background-color: #2171ce; /*var(--main-bg-color-focus);*/ + border: solid 1px #2171ce; /*var(--main-bg-color-focus);*/ +} + +.simple-page form .submit input { + background-color: #4A90E2; /*var(--main-bg-color);*/ + border: solid 1px #4A90E2; /*var(--main-bg-color-focus);*/ +} + +.simple-page input.normal:focus { + border: solid 1px #2171ce; /*var(--main-bg-color-focus);*/ +} + + .simple-page .danger { + border: 1px solid #C4000A; /*var(--danger-color);*/ + box-shadow: 0 0 1px #C4000A; /*var(--danger-color);*/ +} + +.simple-page .alert { + color: #C4000A; /*var(--danger-color);*/ +} \ No newline at end of file diff --git a/war/src/main/webapp/images/jenkins.svg b/war/src/main/webapp/images/jenkins.svg new file mode 100644 index 0000000000..c9ee5d084a --- /dev/null +++ b/war/src/main/webapp/images/jenkins.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/war/src/main/webapp/scripts/loading.js b/war/src/main/webapp/scripts/loading.js new file mode 100644 index 0000000000..282f3395e4 --- /dev/null +++ b/war/src/main/webapp/scripts/loading.js @@ -0,0 +1,60 @@ +/* + * The MIT License + * + * Copyright (c) 2018, CloudBees, Inc. + * + * 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. + */ +const Fetch = { + get: function (url, callback) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onload = function () { + const status = xhr.status; + if(callback) { + callback(null, status); + } + }; + xhr.onerror = function(){ + const message = xhr.responseText; + const status = xhr.status; + if(callback) { + callback(message, status); + } + }; + xhr.send(); + } +}; + +function safeRedirector(url) { + const timeout = 5000; + window.setTimeout(function() { + const statusChecker = arguments.callee; + Fetch.get(url, function(error, status) { + if(status === 503 || status === 0) { + window.setTimeout(statusChecker, timeout) + } else { + window.location.replace(url); + } + if(error) { + console.error(error); + } + }) + }, timeout); +}