Refine the 'Users' Manage Jenkins page (#26475)

* Refine Add User page

* Push

* Push

* Push

* Refine

* Push

* Update index.js

* Refine

* Update index.jelly

* Push

* Refine dialog

* Refine

* Refine

* Update _entryForm.jelly

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>

* Update index.js

* Sync dialog code with app-bar PR

* Remove dead captcha code

* Remove blurb from resource bundles

* Fix dialog text colour

* Remove old email i18n

* Restore Delete button, remove overflow button

* Standardise on Username

* Update index.js

* Don't disable the delete button

* Update index.js

* Update index_de.properties

Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com>

* Remove empty i18n files

---------

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
Co-authored-by: Tim Jacomb <timjacomb1@gmail.com>
Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-04-18 09:52:34 +01:00
1 parent f0f08e398c
commit a5ca785a74
73 files changed
+340 -303

No files matched your search

@@ -312,7 +312,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
*/
@RequirePOST
public void doCreateAccountByAdmin(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException {
createAccountByAdmin(req, rsp, "addUser.jelly", "."); // send the user back to the listing page on success
createAccountByAdmin(req, rsp, "addUserDialog.jelly", "."); // send the user back to the listing page on success
}
/**
@@ -36,7 +36,7 @@ THE SOFTWARE.
<t:editableDescription permission="${app.ADMINISTER}" hideButton="true"/>
<div>
${%Jenkins User ID}: ${it.id}
${%Username}: ${it.id}
</div>
<!-- give properties a chance to contribute summary item -->
<j:forEach var="p" items="${it.allProperties}">
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=\
Идентификатор на потребител на Jenkins
Username=\
Потребител
Groups=\
Групи
@@ -1,3 +0,0 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Id d''''usuari a Jenkins
@@ -1,23 +0,0 @@
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, 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.
Jenkins\ User\ Id=Jenkins Id uživatele
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Jenkins BrugerID
Username=Brugernavn
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Jenkins Benutzer Id
Username=Benutzername
Groups=Gruppen
@@ -1,3 +0,0 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Κωδικός Χρήστη Jenkins
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Id de Usuario Jenkins
Username=Usuario
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Jenkins käyttäjätunnus
Username=Käyttäjätunnus
@@ -21,4 +21,4 @@
# THE SOFTWARE.
Groups=Groupes
Jenkins\ User\ Id=Identifiant d''utilisateur Jenkins
Username=Nom d''utilisateur
@@ -1,23 +0,0 @@
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, 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.
Jenkins\ User\ Id=Jenkins azonosító
@@ -22,4 +22,4 @@
# THE SOFTWARE.
Groups=Gruppi
Jenkins\ User\ ID=ID utente Jenkins
Username=Nome utente
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Jenkins ユーザーID
Username=ユーザー名
Groups=グループ
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Jenkins 사용자 Id
Username=계정명
@@ -1,3 +0,0 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Jenkins vartotojo Id
@@ -1,23 +0,0 @@
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, 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.
Jenkins\ User\ Id=Jenkins lietotāja identifikators
@@ -1,23 +0,0 @@
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, 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.
Jenkins\ User\ Id=Jenkins bruker-id
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Jenkins gebruikersnaam
Username=Gebruikersnaam
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Groups=Grupy
Jenkins\ User\ ID=Identyfikator użytkownika Jenkins
Username=Login
@@ -21,4 +21,4 @@
# THE SOFTWARE.
Groups=Grupos
Jenkins\ User\ ID=ID\ do\ usuário\ do\ Jenkins
Username=Nome de usuário
@@ -1,3 +0,0 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Id Utilizador Jenkins
@@ -21,4 +21,4 @@
# THE SOFTWARE.
Groups=Группы
Jenkins\ User\ Id=Идентификатор пользователя
Username=Имя пользователя
@@ -1,3 +0,0 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Prihlasovacie meno do Jenkins
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
Groups=Групе
Jenkins\ User\ Id=Jenkins идентификациони број
Username=Корисничко име
@@ -1,5 +1,4 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Jenkins användar-ID
Jenkins\ User\ ID=Jenkins användar-ID
Username=Användarnamn
Groups=Grupper
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
Jenkins\ User\ Id=Jenkins Kullanıcı kimliği
Username=Kullanıcı Adı
@@ -1,23 +0,0 @@
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, 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.
Jenkins\ User\ Id=Код користувача в Дженкінс:
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Jenkins\ User\ Id=Jenkins 使用者 ID
Username=使用者名稱
Groups=群組
@@ -29,85 +29,62 @@ THE SOFTWARE.
<st:attribute name="it" use="required" type="hudson.security.HudsonPrivateSecurityRealm">
Context where the page is loaded.
</st:attribute>
<st:attribute name="title" use="required"> <![CDATA[
Title of the HTML page. Rendered in the page content inside a <h1>.
<st:attribute name="title" use="optional"> <![CDATA[
Optional title of the form. Rendered in the page content inside a <h1>.
]]>
</st:attribute>
<st:attribute name="action" use="required">
The method to call from within the HudsonPrivateSecurityRealm.
</st:attribute>
<st:attribute name="captcha" use="optional">
Determines if the tag will include the captcha.
</st:attribute>
<st:attribute name="data" use="optional" type="hudson.security.HudsonPrivateSecurityRealm.SignupInfo">
The wrapper for the data provided by the user and the error message(s) if any. Is null on first form display.
</st:attribute>
</st:documentation>
<h1>${title}</h1>
<div class="form-content">
<table>
<tr>
<f:entry title="${%Username}" class="${data.errors.containsKey('username') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="text" name="username" id="username" value="${data.username}" autofocus="autofocus" />
</f:entry>
<j:if test="${data.errors.containsKey('username')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('username')}
</div>
</j:if>
</tr>
<tr>
<f:entry title="${%Password}" class="${data.errors.containsKey('password1') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="password" name="password1" value="${data.password1}" />
</f:entry>
<j:if test="${data.errors.containsKey('password1')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('password1')}
</div>
</j:if>
</tr>
<tr>
<f:entry title="${%Confirm password}" class="${data.errors.containsKey('password1') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="password" name="password2" value="${data.password2}" />
</f:entry>
<j:if test="${data.errors.containsKey('password1')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('password1')}
</div>
</j:if>
</tr>
<tr>
<f:entry title="${%Full name}" class="${data.errors.containsKey('fullname') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="text" name="fullname" value="${data.fullname}" />
</f:entry>
<j:if test="${data.errors.containsKey('fullname')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('fullname')}
</div>
</j:if>
</tr>
<j:if test="${it.mailerPluginPresent}">
<tr>
<f:entry title="${%E-mail address}" class="${data.errors.containsKey('email') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="text" name="email" value="${data.email}" />
</f:entry>
<j:if test="${data.errors.containsKey('email')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('email')}
</div>
</j:if>
</tr>
</j:if>
<j:if test="${captcha}">
<tr>
<td>${%Enter text as shown}:</td>
<td>
<input class="jenkins-input" type="text" name="captcha" autocomplete="off" /><br />
<img src="${rootURL}/securityRealm/captcha" alt="[${%captcha}]" />
</td>
</tr>
</j:if>
</table>
</div>
<j:if test="${attrs.title != null}">
<h1>${title}</h1>
</j:if>
<f:entry title="${%Username}" class="${data.errors.containsKey('username') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="text" name="username" id="username" value="${data.username}" autofocus="autofocus" />
</f:entry>
<j:if test="${data.errors.containsKey('username')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('username')}
</div>
</j:if>
<f:entry title="${%Password}" class="${data.errors.containsKey('password1') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="password" name="password1" value="${data.password1}" />
</f:entry>
<j:if test="${data.errors.containsKey('password1')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('password1')}
</div>
</j:if>
<f:entry title="${%Confirm password}" class="${data.errors.containsKey('password1') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="password" name="password2" value="${data.password2}" />
</f:entry>
<j:if test="${data.errors.containsKey('password1')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('password1')}
</div>
</j:if>
<f:entry title="${%Full name}" class="${data.errors.containsKey('fullname') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="text" name="fullname" value="${data.fullname}" />
</f:entry>
<j:if test="${data.errors.containsKey('fullname')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('fullname')}
</div>
</j:if>
<j:if test="${it.mailerPluginPresent}">
<f:entry title="${%Email}" class="${data.errors.containsKey('email') ? 'jenkins-!-margin-bottom-2' : ''}">
<input class="jenkins-input" type="text" name="email" value="${data.email}" />
</f:entry>
<j:if test="${data.errors.containsKey('email')}">
<div class="error jenkins-!-margin-bottom-2">
${data.errors.get('email')}
</div>
</j:if>
</j:if>
</j:jelly>
@@ -37,9 +37,6 @@ THE SOFTWARE.
<st:attribute name="action" use="required">
The method to call from within the HudsonPrivateSecurityRealm.
</st:attribute>
<st:attribute name="captcha" use="optional" type="boolean">
Determines if the tag will include the captcha.
</st:attribute>
<st:attribute name="data" use="optional" type="hudson.security.HudsonPrivateSecurityRealm.SignupInfo">
The wrapper for the data provided by the user and the error message(s) if any. Is null on first form display.
</st:attribute>
@@ -48,7 +45,7 @@ THE SOFTWARE.
<l:breadcrumb title="${title}"/>
<l:main-panel>
<form action="${rootURL}/securityRealm/${action}" method="post" class="jenkins-form jenkins-form-item--small jenkins-!-margin-auto">
<local:_entryForm title="${title}" action="${action}" captcha="${captcha}" it="${host}" data="${data}" />
<local:_entryForm title="${title}" action="${action}" it="${host}" data="${data}" />
<f:bottomButtonBar>
<f:submit value="${title}" />
</f:bottomButtonBar>
@@ -20,8 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
E-mail\ address=\
Адрес на е-поща
Password=\
Парола
Username=\
@@ -25,4 +25,3 @@ Full\ name=Navn
Username=Brugernavn
Enter\ text\ as\ shown=Indtast den viste tekst
Confirm\ password=Gentag adgangskode
E-mail\ address=E-mail adresse
@@ -2,5 +2,4 @@ Username=Benutzer
Password=Passwort
Confirm\ password=Passwort-Wiederholung
Full\ name=Voller Name
E-mail\ address=E-Mail-Adresse
Enter\ text\ as\ shown=Text wie angezeigt eingeben
@@ -24,6 +24,5 @@ Username=Usuario
Password=Contraseña
Confirm\ password=Confirma la contraseña
Full\ name=Nombre completo
E-mail\ address=Dirección de email
Enter\ text\ as\ shown=Introdude el texto mostrado
Sign\ up=Crear una cuenta
@@ -1,7 +1,6 @@
# This file is under the MIT License by authors
Confirm\ password=Varmista salasana
E-mail\ address=Sähköpostiosoite
Full\ name=Koko nimi
Password=Salasana
Username=Käyttäjätunnus
@@ -24,5 +24,3 @@ Username=Nom d''utilisateur
Password=Mot de passe
Confirm\ password=Confirmation du mot de passe
Full\ name=Nom complet
E-mail\ address=Adresse courriel
Enter\ text\ as\ shown=
@@ -23,7 +23,7 @@
captcha=CAPTCHA
Confirm\ password=Conferma password
E-mail\ address=Indirizzo di posta elettronica
Email=Indirizzo di posta elettronica
Enter\ text\ as\ shown=Immettere il testo così come viene visualizzato
Full\ name=Nome completo
Password=Password
@@ -25,5 +25,4 @@ Username=ユーザー名
Password=パスワード
Confirm\ password=パスワードの確認
Full\ name=フルネーム
E-mail\ address=メールアドレス
Enter\ text\ as\ shown=テキスト
@@ -21,7 +21,6 @@
# THE SOFTWARE.
Confirm\ password=암호 확인
E-mail\ address=이메일 주소
Enter\ text\ as\ shown=아래 보이는 문자 입력
Full\ name=이름
Password=암호
@@ -1,7 +1,6 @@
# This file is under the MIT License by authors
Confirm\ password=Bevestig wachtwoord
E-mail\ address=E-mailadres
Full\ name=Naam
Password=Wachtwoord
Username=Gebruikersnaam
@@ -21,7 +21,6 @@
# THE SOFTWARE.
Confirm\ password=Powtórz hasło
E-mail\ address=Adres e-mail
Full\ name=Pełna nazwa
Password=Hasło
Username=Login
@@ -25,5 +25,5 @@ Username=Nome de usuário
Enter\ text\ as\ shown=Digite o texto como mostrado
Full\ name=Nome completo
Confirm\ password=Confirmar a senha
E-mail\ address=Endereço de e-mail
Email=Email
captcha=captcha
@@ -21,7 +21,6 @@
# THE SOFTWARE.
Confirm\ password=Повторите пароль
E-mail\ address=Адрес электронной почты
Full\ name=Ф.И.О.
Password=Пароль
Username=Имя пользователя
@@ -5,5 +5,4 @@ Password=Лозинка
Confirm\ password=Потврдите лозинку
Full\ name=Име и презиме
Enter\ text\ as\ shown=Унесите текст како је приказан
E-mail\ address=Адреса е-поште
Sign\ up=Регистрација
@@ -24,5 +24,4 @@ Username=Kullanıcı Adı
Password=Şifre
Confirm\ password=Şifreyi Doğrula
Full\ name=Tam İsim
E-mail\ address=E-posta Adresi
Enter\ text\ as\ shown=Metni gösterildiği gibi girin
@@ -26,5 +26,5 @@ Username=使用者名稱
Password=密碼
Confirm\ password=確認密碼
Full\ name=全名
E-mail\ address=電子郵件信箱
Email=電子信箱
Enter\ text\ as\ shown=輸入下列文字
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<local:_entryFormPage host="${it}" title="${%Create User}" action="createAccountByAdmin" captcha="${false}" data="${data}" />
<local:_entryFormPage host="${it}" title="${%Create User}" action="createAccountByAdmin" data="${data}" />
</j:jelly>
@@ -0,0 +1,38 @@
<!--
The MIT License
Copyright (c) 2026, Jan Faracik
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.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:st="jelly:stapler"
xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" xmlns:f="/lib/form">
<st:setHeader name="X-Dialog-Title" value="${%Create User}" />
<l:ajax>
<form action="${rootURL}/securityRealm/createAccountByAdmin" method="post">
<local:_entryForm action="createAccountByAdmin" it="${it}" data="${data}" />
<f:bottomButtonBar>
<f:submit value="${%Create}" />
</f:bottomButtonBar>
</form>
</l:ajax>
</j:jelly>
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<local:_entryFormPage host="${it}" title="${%Create First Admin User}" action="createFirstAccount" captcha="${false}" data="${data}"/>
<local:_entryFormPage host="${it}" title="${%Create First Admin User}" action="createFirstAccount" data="${data}"/>
</j:jelly>
@@ -28,14 +28,14 @@ THE SOFTWARE.
<l:view>
<l:app-bar title="${%Users}" subtitle="${it.allUsers.size()}">
<l:isAdmin>
<a href="addUser" class="jenkins-button jenkins-button--primary">
<button class="jenkins-button jenkins-button--primary"
data-type="dialog-opener"
data-dialog-url="addUserDialog">
<l:icon src="symbol-add" />
${%Create User}
</a>
</button>
</l:isAdmin>
</l:app-bar>
<p class="jenkins-page-description">${%blurb}</p>
</l:view>
</j:set>
@@ -44,8 +44,8 @@ THE SOFTWARE.
<thead>
<tr>
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
<th initialSortDir="down">${%User ID}</th>
<th>${%Name}</th>
<th initialSortDir="down">${%Username}</th>
<th>${%Full name}</th>
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
<th class="jenkins-table__cell--tight" data-sort-disable="true" />
</tr>
@@ -59,14 +59,14 @@ THE SOFTWARE.
</div>
</td>
<td>
<a href="${user.url}/" class="jenkins-table__link model-link inside">${user.id}</a>
<a href="${user.url}/">${user.id}</a>
</td>
<td>
${user.fullName}
</td>
<td>
<div class="jenkins-table__cell__button-wrapper">
<a href="${user.url}/" class="jenkins-button jenkins-button--tertiary">
<a href="${user.url}/account" class="jenkins-button jenkins-button--tertiary">
<l:icon src="symbol-settings" />
</a>
</div>
@@ -74,8 +74,12 @@ THE SOFTWARE.
<td>
<j:if test="${user.canDelete()}">
<div class="jenkins-table__cell__button-wrapper">
<l:confirmationLink href="${user.url}/doDelete" class="jenkins-button jenkins-button--tertiary jenkins-!-destructive-color"
post="true" message="${%delete.user(user.displayName)}">
<l:confirmationLink href="${user.url}/doDelete"
class="jenkins-button jenkins-button--tertiary jenkins-!-destructive-color"
post="true"
title="${%delete.user(user.displayName)}"
okText="${%Delete}"
destructive="true">
<l:icon src="symbol-trash" />
</l:confirmationLink>
</div>
@@ -20,8 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=\
These users can log into Jenkins. This is a sub set of <a href="../asynchPeople">this list</a>, \
which also contains auto-created users who really just made some commits on some projects and have no \
direct Jenkins access.
delete.user=Delete Jenkins user ‘{0}’?
@@ -24,10 +24,5 @@ Name=\
Име
Users=\
Потребители
blurb=\
Тези потребители могат да се впишат в Jenkins. Това е съкратена версия на\
<a href="../asynchPeople">този списък</a>, който съдържа също и автоматично\
създадените потребители, които някога са подавали промени по някои проекти и\
нямат директен достъп до Jenkins.
User\ Id=\
Идентификатор
@@ -20,5 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=Disse brugere kan logge på Jenkins. Dette er en delmængde af <a href="../asynchPeople">denne liste</a>
Name=Navn
@@ -20,8 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=\
Diese Benutzer können sich bei Jenkins anmelden. Dies ist eine Untermenge <a href="../asynchPeople">jener Liste</a>, die zusätzlich automatisch angelegte Benutzer enthalten kann. Diese Benutzer haben zwar Commits zu Projekten beigetragen, dürfen sich aber nicht direkt bei Jenkins anmelden.
Name=Name
User\ Id=Benutzer-ID
Users=Benutzer
@@ -20,10 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=\
Estos usuarios pueden entrar en Jenkins. Este es un subconjunto de <a href="../asynchPeople">esta lista</a>, \
que tambien incluyen usuarios creados automáticamente porque hayan hecho ''commits'' a proyectos. \
Los usuarios creados automáticamente no tienen acceso directo a Jenkins.
Name=Nombre
Users=Usuarios
User\ Id=ID usuario
@@ -21,5 +21,4 @@
# THE SOFTWARE.
Users=Utilisateurs
blurb=Ces utilisateurs peuvent se logguer sur Jenkins. C''est le groupe contenant <a href="../asynchPeople">cette liste</a>, qui contient également les utilisateurs créés automatiquement qui ont simplement fait des commits sur certains projets et n''ont pas d''accès direct à Jenkins.
Name=Nom
@@ -21,10 +21,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=Questi utenti possono accedere a Jenkins. Questo è un sottoinsieme \
di <a href="../asynchPeople">quest''elenco</a>, che contiene anche gli \
utenti creati automaticamente che hanno solamente eseguito dei commit in \
alcuni progetti e non hanno accesso diretto a Jenkins.
Name=Nome
User\ ID=ID utente
Users=Utenti
@@ -21,8 +21,5 @@
# THE SOFTWARE.
User\ Id=ユーザーID
blurb=\
これらのユーザーはJenkinsにログインすることができます。これは、Jenkinsにアクセスしていないけれども \
いくつかのプロジェクトにコミットすると自動的に作成されるユーザーを含む<a href="../asynchPeople">このリスト</a>の一部です。
Name=名前
Users=ユーザー
@@ -23,4 +23,3 @@
Name=이름
User\ Id=사용자 ID
Users=사용자
blurb=이 사용자들은 Jenkins 에 로그인 할 수 있습니다. 이 사용자들은 <a href="../asynchPeople">개발자 목록</a>의 일부입니다. 개발자 목록은 단지 커밋만 하고 직접 Jenkins 에 접속하지 않은 자동 생성된 사람들을 포함합니다.
@@ -23,4 +23,3 @@
Name=Navn
User\ Id=Bruker-id
Users=Brukere
blurb=Disse brukerne kan logge inn på Jenkins. De er et subsett av <a href="../asynchPeople">dene listen</a>, som også inneholder autogenererte brukere som egentlig bare har sjekket inn på noen prosjekter og ikke har direkte jenkins-tilgang.
@@ -3,4 +3,3 @@
Name=Naam
User\ Id=Gebruikersnaam
Users=Gebruikers
blurb=Deze gebruikers kunnen op Jenkins inloggen. Dit is een sub set van <a href="../asynchPeople">deze lijst</a>, deze bevat ook automatisch aangemaakte gebruikers die hebben bijgedragen bij een of meer projecten maar die geen directe toegang tot Jenkins hebben.
@@ -22,6 +22,5 @@
Name=Nome
Users=Usuários
blurb=Esses usuários podem entrar no Jenkins. Este é um sub-conjunto desta <a href="../asynchPeople">lista</a>, que também contém os usuários criados de forma automática ao executarem alguma confirmação nos projetos que o Jenkins tem acesso.
User\ ID=Identificação do usuário
Create\ User=Criar Usuário
@@ -3,4 +3,3 @@
Name=Nome
User\ Id=Id Utilizador
Users=Utilizadores
blurb=Estes utilizadores conseguem autenticar-se no Jenkins. Este é um sub conjunto <a href="../asynchPeople">desta lista</a>, que também contém utilizadores auto-criados que só realizaram alguns commits nalgum projecto e não têm acesso directo no Jenkins.
@@ -23,4 +23,3 @@
Name=Имя
User\ Id=ID пользователя
Users=Пользователи
blurb=Перечисленные пользователи могут входить в Jenkins. Указанный список является подмножеством <a href="../asynchPeople">полного списка пользователей</a>, который в дополнение содержит автоматически созданных пользователей, которые вносили изменения в проекты (делали commit в SCM), и могут не иметь доступа к данному экземпляру Jenkins.
@@ -1,8 +1,5 @@
# This file is under the MIT License by authors
Users=Корисници
blurb=\
Ови корисниви могу се пријавити на Jenkins, што је подскуп <a href="../asynchPeople">овог списка</a>, \
који садржи и аутоматски-креиране кориснике без кирекног приступа на Jenkins.
User\ Id=Идентификациони број корисника
Name=Име
@@ -20,10 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=\
Dessa användare kan logga in på Jenkins. Detta är en delmängd av <a href="../asynchPeople">denna lista</a> \
som även innehåller automatiskt skapade användare som egentligen bara gjort några incheckningar på vissa projekt\
och saknar direkt åtkomst till Jenkins.
delete.user=Radera Jenkins-användaren "{0}"?
Name=Namn
@@ -22,9 +22,5 @@
Users=Kullanıcılar
Create\ User=Kullanıcı Oluştur
blurb=\
Bu kullanıcılar Jenkins''de oturum açabilir. Bu liste, <a href="../asynchPeople">buradakinin</a> bir üst kümesidir, \
aynı zamanda bazı projelerde commit işlemi yapan, fakat direk Jenkins erişimi olmayan ve otomatik olarak oluşturulan \
kullanıcıları da içerir
User\ ID=Kullanıcı ID
Name=İsim
@@ -22,8 +22,5 @@
# THE SOFTWARE.
Users=使用者
blurb=\
這些使用者可以登入 Jenkins。這是<a href="../asynchPeople">這份清單</a>的子集合,\
該清單包含自動建立,只在某些專案上 Commit 東西的使用者,這類使用者不能直接使用 Jenkins。
User\ Id=使用者識別碼
Name=名稱
@@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm">
<local:_entryFormPage host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" data="${data}" />
<local:_entryFormPage host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" data="${data}" />
</j:jelly>
@@ -5,7 +5,7 @@
<l:main-panel>
<div class="create-admin-user">
<form action="${rootURL}/setupWizard/createAdminUser" method="post">
<local:_entryForm it="${app.securityRealm}" host="${app.securityRealm}" title="${%Create First Admin User}" captcha="${false}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
<local:_entryForm it="${app.securityRealm}" host="${app.securityRealm}" title="${%Create First Admin User}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</form>
</div>
</l:main-panel>
+211 -1
View File
@@ -331,6 +331,12 @@ function init() {
let dialog = new Dialog("form", options);
return dialog.show();
},
wizard: function (initialUrl, options) {
dialog.modal(document.createElement("template"), options);
navigateToNextPage(initialUrl, "");
},
};
behaviorShim.specify(
@@ -339,7 +345,14 @@ function init() {
1000,
(element) => {
element.addEventListener("click", () => {
renderOnDemandDialog(element.dataset.dialogId);
if (element.dataset.dialogUrl != null) {
window.dialog.wizard(element.dataset.dialogUrl, {
minWidth: "min(550px, 100vw)",
preventCloseOnOutsideClick: true,
});
} else {
renderOnDemandDialog(element.dataset.dialogId);
}
});
},
);
@@ -357,4 +370,201 @@ function init() {
}
}
function updateWizardTitle(titleText) {
if (titleText == null) {
return;
}
const title = document.querySelector(
".jenkins-dialog .jenkins-dialog__title > span",
);
if (title != null) {
title.textContent = titleText;
}
}
/** Resolve a relative wizard form action against the current step URL. */
function resolveWizardFormAction(form, baseUrl) {
const formAction = form.getAttribute("action");
if (
formAction &&
!formAction.startsWith("/") &&
!formAction.startsWith("http")
) {
form.action = new URL(formAction, baseUrl).toString();
}
}
function submitWizardForm(form) {
const jsonInputName = "json";
let jsonInput = form.elements.namedItem(jsonInputName);
if (jsonInput == null) {
jsonInput = document.createElement("input");
jsonInput.type = "hidden";
jsonInput.name = jsonInputName;
form.appendChild(jsonInput);
}
buildFormTree(form);
let body = new FormData(form);
const hasFileInput = Array.from(form.elements).some(
(element) => element instanceof HTMLInputElement && element.type === "file",
);
if (!hasFileInput) {
body = new URLSearchParams(body);
}
fetch(form.action, {
method: form.method.toUpperCase(),
headers: crumb.wrap({}),
body: body,
}).then((rsp) => {
if (rsp.redirected) {
window.location.assign(rsp.url);
return;
}
rsp.text().then((responseText) => {
const replacementForm = renderWizardForm({
responseText,
requestUrl: rsp.url,
titleText: rsp.headers.get("X-Dialog-Title"),
replaceExistingForm: form,
});
if (replacementForm == null) {
window.location.assign(rsp.url);
}
});
});
}
function configureWizardForm(form) {
form.addEventListener("submit", (e) => {
e.preventDefault();
submitWizardForm(e.currentTarget);
});
}
function renderWizardForm({
responseText,
requestUrl,
titleText,
replaceExistingForm = null,
hideExistingForms = false,
}) {
const dialogContents = document.querySelector(
".jenkins-dialog .jenkins-dialog__contents",
);
const newDialog = document.createElement("div");
newDialog.innerHTML = responseText;
const form = newDialog.querySelector("form");
if (form == null) {
return null;
}
if (hideExistingForms) {
Array.from(dialogContents.children)
.filter((element) => element.tagName === "FORM")
.forEach((existingForm) => existingForm.classList.add("jenkins-hidden"));
}
resolveWizardFormAction(form, requestUrl);
updateWizardTitle(titleText);
configureWizardForm(form);
// Recreate script tags while the form is still detached, so each script
// executes exactly once, at the moment the form is inserted into the dialog.
recreateScripts(form);
if (replaceExistingForm != null) {
replaceExistingForm.replaceWith(form);
} else {
dialogContents.appendChild(form);
}
wireCancelButton(form);
return form;
}
function wireCancelButton(form) {
const dialog = form.closest("dialog");
form.querySelector("[data-id=cancel]")?.addEventListener("click", (e) => {
e.preventDefault();
dialog?.dispatchEvent(new Event("cancel"));
});
}
function navigateToNextPage(url) {
fetch(url, {
method: "GET",
headers: crumb.wrap({}),
}).then((rsp) => {
if (rsp.ok) {
rsp.text().then((responseText) => {
const form = renderWizardForm({
responseText,
requestUrl: rsp.url,
titleText: rsp.headers.get("X-Dialog-Title"),
hideExistingForms: true,
});
if (form == null) {
window.location.assign(rsp.url);
}
});
} else {
console.error(
"Failed to load dialog content, response from API is:",
rsp,
);
}
});
}
/*
* Recreate script tags to ensure they are executed, as innerHTML does not execute scripts.
*
*/
function recreateScripts(form) {
const scripts = Array.from(form.getElementsByTagName("script"));
if (scripts.length === 0) {
Behaviour.applySubtree(form, true);
return;
}
for (let i = 0; i < scripts.length; i++) {
const original = scripts[i];
const script = document.createElement("script");
for (let j = 0; j < original.attributes.length; j++) {
script.setAttribute(
original.attributes[j].name,
original.attributes[j].value,
);
}
if (original.text) {
script.text = original.text;
}
// only attach the load listener to the last script to avoid multiple calls to Behaviour.applySubtree
if (i === scripts.length - 1) {
script.addEventListener("load", () => {
setTimeout(() => {
Behaviour.applySubtree(form, true);
if (form.method.toLowerCase() !== "get") {
form.onsubmit = null; // clear any existing handler
}
}, 50);
});
}
original.parentNode.replaceChild(script, original);
}
}
export default { init };
+1
View File
@@ -14,6 +14,7 @@ $jenkins-dialog-padding: 1.25rem;
padding: 0;
display: flex;
flex-direction: column;
color: var(--text-color);
// Thin highlight for dark themes to differentiate the dialog from its backdrop
outline: 1px solid rgb(255 255 255 / 0.05);