1
0
mirror of https://github.com/Stirling-Tools/Stirling-PDF.git synced 2024-09-21 20:30:12 +02:00

Merge pull request #1201 from Ludy87/add_functions_oauth2

extends the functionality of oauth in Stirling PDF
This commit is contained in:
Anthony Stirling 2024-05-18 23:30:37 +01:00 committed by GitHub
commit 6fc9a2032a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 1552 additions and 762 deletions

View File

@ -15,7 +15,14 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
private static final List<String> ALLOWED_PARAMS = private static final List<String> ALLOWED_PARAMS =
Arrays.asList( Arrays.asList(
"lang", "endpoint", "endpoints", "logout", "error", "file", "messageType"); "lang",
"endpoint",
"endpoints",
"logout",
"error",
"erroroauth",
"file",
"messageType");
@Override @Override
public boolean preHandle( public boolean preHandle(

View File

@ -3,27 +3,31 @@ package stirling.software.SPDF.config.security;
import java.io.IOException; import java.io.IOException;
import java.util.Optional; import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired private final LoginAttemptService loginAttemptService; private LoginAttemptService loginAttemptService;
@Autowired private final UserService userService; // Inject the UserService private UserService userService;
private static final Logger logger =
LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
public CustomAuthenticationFailureHandler( public CustomAuthenticationFailureHandler(
LoginAttemptService loginAttemptService, UserService userService) { final LoginAttemptService loginAttemptService, UserService userService) {
this.loginAttemptService = loginAttemptService; this.loginAttemptService = loginAttemptService;
this.userService = userService; this.userService = userService;
} }
@ -34,22 +38,33 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
HttpServletResponse response, HttpServletResponse response,
AuthenticationException exception) AuthenticationException exception)
throws IOException, ServletException { throws IOException, ServletException {
String ip = request.getRemoteAddr(); String ip = request.getRemoteAddr();
logger.error("Failed login attempt from IP: " + ip); logger.error("Failed login attempt from IP: {}", ip);
if (exception.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)
|| "Password must not be null".equalsIgnoreCase(exception.getMessage())) {
response.sendRedirect("/login?error=oauth2AuthenticationError");
return;
}
String username = request.getParameter("username"); String username = request.getParameter("username");
if (!isDemoUser(username)) { if (username != null && !isDemoUser(username)) {
if (loginAttemptService.loginAttemptCheck(username)) { logger.info(
setDefaultFailureUrl("/login?error=locked"); "Remaining attempts for user {}: {}",
username,
} else { loginAttemptService.getRemainingAttempts(username));
if (exception.getClass().isAssignableFrom(LockedException.class)) { loginAttemptService.loginFailed(username);
setDefaultFailureUrl("/login?error=locked"); if (loginAttemptService.isBlocked(username)
} || exception.getClass().isAssignableFrom(LockedException.class)) {
response.sendRedirect("/login?error=locked");
return;
} }
} }
if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) { if (exception.getClass().isAssignableFrom(BadCredentialsException.class)
setDefaultFailureUrl("/login?error=badcredentials"); || exception.getClass().isAssignableFrom(UsernameNotFoundException.class)) {
response.sendRedirect("/login?error=badcredentials");
return;
} }
super.onAuthenticationFailure(request, response, exception); super.onAuthenticationFailure(request, response, exception);

View File

@ -2,11 +2,9 @@ package stirling.software.SPDF.config.security;
import java.io.IOException; import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -14,25 +12,30 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.utils.RequestUriUtils; import stirling.software.SPDF.utils.RequestUriUtils;
@Component
public class CustomAuthenticationSuccessHandler public class CustomAuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler { extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired private LoginAttemptService loginAttemptService; private LoginAttemptService loginAttemptService;
public CustomAuthenticationSuccessHandler(LoginAttemptService loginAttemptService) {
this.loginAttemptService = loginAttemptService;
}
@Override @Override
public void onAuthenticationSuccess( public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication) HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws ServletException, IOException { throws ServletException, IOException {
String username = request.getParameter("username");
loginAttemptService.loginSucceeded(username); String userName = request.getParameter("username");
loginAttemptService.loginSucceeded(userName);
// Get the saved request // Get the saved request
HttpSession session = request.getSession(false); HttpSession session = request.getSession(false);
SavedRequest savedRequest = SavedRequest savedRequest =
session != null (session != null)
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
: null; : null;
if (savedRequest != null if (savedRequest != null
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) { && !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
// Redirect to the original destination // Redirect to the original destination

View File

@ -2,10 +2,9 @@ package stirling.software.SPDF.config.security;
import java.io.IOException; import java.io.IOException;
import org.springframework.context.annotation.Bean; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
@ -14,10 +13,8 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
@Bean
public SessionRegistry sessionRegistry() { @Autowired SessionRegistry sessionRegistry;
return new SessionRegistryImpl();
}
@Override @Override
public void onLogoutSuccess( public void onLogoutSuccess(
@ -26,14 +23,11 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
HttpSession session = request.getSession(false); HttpSession session = request.getSession(false);
if (session != null) { if (session != null) {
String sessionId = session.getId(); String sessionId = session.getId();
sessionRegistry().removeSessionInformation(sessionId); sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.debug("Session invalidated: " + sessionId);
} }
if (request.getParameter("oauth2AutoCreateDisabled") != null) { response.sendRedirect(request.getContextPath() + "/login?logout=true");
response.sendRedirect(
request.getContextPath() + "/login?error=oauth2AutoCreateDisabled");
} else {
response.sendRedirect(request.getContextPath() + "/login?logout=true");
}
} }
} }

View File

@ -39,6 +39,10 @@ public class CustomUserDetailsService implements UserDetailsService {
"Your account has been locked due to too many failed login attempts."); "Your account has been locked due to too many failed login attempts.");
} }
if (!user.hasPassword()) {
throw new IllegalArgumentException("Password must not be null");
}
return new org.springframework.security.core.userdetails.User( return new org.springframework.security.core.userdetails.User(
user.getUsername(), user.getUsername(),
user.getPassword(), user.getPassword(),

View File

@ -7,6 +7,8 @@ import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -19,32 +21,16 @@ public class InitialSecuritySetup {
@Autowired private UserService userService; @Autowired private UserService userService;
@Autowired ApplicationProperties applicationProperties; @Autowired private ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(InitialSecuritySetup.class);
@PostConstruct @PostConstruct
public void init() { public void init() {
if (!userService.hasUsers()) { if (!userService.hasUsers()) {
initializeAdminUser();
String initialUsername =
applicationProperties.getSecurity().getInitialLogin().getUsername();
String initialPassword =
applicationProperties.getSecurity().getInitialLogin().getPassword();
if (initialUsername != null && initialPassword != null) {
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
} else {
initialUsername = "admin";
initialPassword = "stirling";
userService.saveUser(
initialUsername, initialPassword, Role.ADMIN.getRoleId(), true);
}
}
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
userService.saveUser(
Role.INTERNAL_API_USER.getRoleId(),
UUID.randomUUID().toString(),
Role.INTERNAL_API_USER.getRoleId());
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
} }
initializeInternalApiUser();
} }
@PostConstruct @PostConstruct
@ -56,6 +42,51 @@ public class InitialSecuritySetup {
} }
} }
private void initializeAdminUser() {
String initialUsername =
applicationProperties.getSecurity().getInitialLogin().getUsername();
String initialPassword =
applicationProperties.getSecurity().getInitialLogin().getPassword();
if (initialUsername != null
&& !initialUsername.isEmpty()
&& initialPassword != null
&& !initialPassword.isEmpty()
&& !userService.findByUsernameIgnoreCase(initialUsername).isPresent()) {
try {
if (userService.isUsernameValid(initialUsername)) {
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
logger.info("Admin user created: " + initialUsername);
}
} catch (IllegalArgumentException e) {
logger.error("Failed to initialize security setup", e);
System.exit(1);
}
} else {
createDefaultAdminUser();
}
}
private void createDefaultAdminUser() {
String defaultUsername = "admin";
String defaultPassword = "stirling";
if (!userService.findByUsernameIgnoreCase(defaultUsername).isPresent()) {
userService.saveUser(defaultUsername, defaultPassword, Role.ADMIN.getRoleId(), true);
logger.info("Default admin user created: " + defaultUsername);
}
}
private void initializeInternalApiUser() {
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
userService.saveUser(
Role.INTERNAL_API_USER.getRoleId(),
UUID.randomUUID().toString(),
Role.INTERNAL_API_USER.getRoleId());
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
logger.info("Internal API user created: " + Role.INTERNAL_API_USER.getRoleId());
}
}
private void saveKeyToConfig(String key) throws IOException { private void saveKeyToConfig(String key) throws IOException {
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
List<String> lines = Files.readAllLines(path); List<String> lines = Files.readAllLines(path);

View File

@ -3,6 +3,8 @@ package stirling.software.SPDF.config.security;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -15,44 +17,62 @@ public class LoginAttemptService {
@Autowired ApplicationProperties applicationProperties; @Autowired ApplicationProperties applicationProperties;
private int MAX_ATTEMPTS; private static final Logger logger = LoggerFactory.getLogger(LoginAttemptService.class);
private int MAX_ATTEMPT;
private long ATTEMPT_INCREMENT_TIME; private long ATTEMPT_INCREMENT_TIME;
private ConcurrentHashMap<String, AttemptCounter> attemptsCache;
@PostConstruct @PostConstruct
public void init() { public void init() {
MAX_ATTEMPTS = applicationProperties.getSecurity().getLoginAttemptCount(); MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount();
ATTEMPT_INCREMENT_TIME = ATTEMPT_INCREMENT_TIME =
TimeUnit.MINUTES.toMillis( TimeUnit.MINUTES.toMillis(
applicationProperties.getSecurity().getLoginResetTimeMinutes()); applicationProperties.getSecurity().getLoginResetTimeMinutes());
attemptsCache = new ConcurrentHashMap<>();
} }
private final ConcurrentHashMap<String, AttemptCounter> attemptsCache =
new ConcurrentHashMap<>();
public void loginSucceeded(String key) { public void loginSucceeded(String key) {
attemptsCache.remove(key); logger.info(key + " " + attemptsCache.mappingCount());
if (key == null || key.trim().isEmpty()) {
return;
}
attemptsCache.remove(key.toLowerCase());
} }
public boolean loginAttemptCheck(String key) { public void loginFailed(String key) {
attemptsCache.compute( if (key == null || key.trim().isEmpty()) return;
key,
(k, attemptCounter) -> { AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
if (attemptCounter == null if (attemptCounter == null) {
|| attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) { attemptCounter = new AttemptCounter();
return new AttemptCounter(); attemptsCache.put(key.toLowerCase(), attemptCounter);
} else { } else {
attemptCounter.increment(); if (attemptCounter.shouldReset(ATTEMPT_INCREMENT_TIME)) {
return attemptCounter; attemptCounter.reset();
} }
}); attemptCounter.increment();
return attemptsCache.get(key).getAttemptCount() >= MAX_ATTEMPTS; }
} }
public boolean isBlocked(String key) { public boolean isBlocked(String key) {
AttemptCounter attemptCounter = attemptsCache.get(key); if (key == null || key.trim().isEmpty()) return false;
if (attemptCounter != null) { AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
return attemptCounter.getAttemptCount() >= MAX_ATTEMPTS; if (attemptCounter == null) {
return false;
} }
return false;
return attemptCounter.getAttemptCount() >= MAX_ATTEMPT;
}
public int getRemainingAttempts(String key) {
if (key == null || key.trim().isEmpty()) return MAX_ATTEMPT;
AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase());
if (attemptCounter == null) {
return MAX_ATTEMPT;
}
return MAX_ATTEMPT - attemptCounter.getAttemptCount();
} }
} }

View File

@ -1,6 +1,5 @@
package stirling.software.SPDF.config.security; package stirling.software.SPDF.config.security;
import java.io.IOException;
import java.util.*; import java.util.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -14,33 +13,30 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ClientRegistrations; import org.springframework.security.oauth2.client.registration.ClientRegistrations;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import jakarta.servlet.ServletException; import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import jakarta.servlet.http.HttpServletRequest; import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
import jakarta.servlet.http.HttpServletResponse; import stirling.software.SPDF.config.security.oauth2.CustomOAuth2LogoutSuccessHandler;
import jakarta.servlet.http.HttpSession; import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.JPATokenRepositoryImpl; import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@ -49,7 +45,7 @@ import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
@EnableMethodSecurity @EnableMethodSecurity
public class SecurityConfiguration { public class SecurityConfiguration {
@Autowired private UserDetailsService userDetailsService; @Autowired private CustomUserDetailsService userDetailsService;
@Bean @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
@ -98,7 +94,8 @@ public class SecurityConfiguration {
formLogin formLogin
.loginPage("/login") .loginPage("/login")
.successHandler( .successHandler(
new CustomAuthenticationSuccessHandler()) new CustomAuthenticationSuccessHandler(
loginAttemptService))
.defaultSuccessUrl("/") .defaultSuccessUrl("/")
.failureHandler( .failureHandler(
new CustomAuthenticationFailureHandler( new CustomAuthenticationFailureHandler(
@ -111,18 +108,7 @@ public class SecurityConfiguration {
new AntPathRequestMatcher("/logout")) new AntPathRequestMatcher("/logout"))
.logoutSuccessHandler(new CustomLogoutSuccessHandler()) .logoutSuccessHandler(new CustomLogoutSuccessHandler())
.invalidateHttpSession(true) // Invalidate session .invalidateHttpSession(true) // Invalidate session
.deleteCookies("JSESSIONID", "remember-me") .deleteCookies("JSESSIONID", "remember-me"))
.addLogoutHandler(
(request, response, authentication) -> {
HttpSession session =
request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry()
.removeSessionInformation(
sessionId);
}
}))
.rememberMe( .rememberMe(
rememberMeConfigurer -> rememberMeConfigurer ->
rememberMeConfigurer // Use the configurator directly rememberMeConfigurer // Use the configurator directly
@ -161,50 +147,43 @@ public class SecurityConfiguration {
.permitAll() .permitAll()
.anyRequest() .anyRequest()
.authenticated()) .authenticated())
.userDetailsService(userDetailsService)
.authenticationProvider(authenticationProvider()); .authenticationProvider(authenticationProvider());
// Handle OAUTH2 Logins // Handle OAUTH2 Logins
if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) { if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) {
http.oauth2Login( http.oauth2Login(
oauth2 -> oauth2 ->
oauth2.loginPage("/oauth2") oauth2.loginPage("/oauth2")
/* /*
This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser'
is set as true, else login fails with an error message advising the same. is set as true, else login fails with an error message advising the same.
*/ */
.successHandler( .successHandler(
new AuthenticationSuccessHandler() { new CustomOAuth2AuthenticationSuccessHandler(
@Override loginAttemptService,
public void onAuthenticationSuccess( applicationProperties,
HttpServletRequest request, userService))
HttpServletResponse response, .failureHandler(
Authentication authentication) new CustomOAuth2AuthenticationFailureHandler())
throws ServletException, IOException { // Add existing Authorities from the database
OAuth2User oauthUser = .userInfoEndpoint(
(OAuth2User) userInfoEndpoint ->
authentication userInfoEndpoint
.getPrincipal(); .oidcUserService(
if (userService.processOAuth2PostLogin( new CustomOAuth2UserService(
oauthUser.getAttribute("email"), applicationProperties,
applicationProperties userService,
.getSecurity() loginAttemptService))
.getOAUTH2() .userAuthoritiesMapper(
.getAutoCreateUser())) { userAuthoritiesMapper())))
response.sendRedirect("/"); .logout(
} else { logout ->
response.sendRedirect( logout.logoutSuccessHandler(
"/logout?oauth2AutoCreateDisabled=true"); new CustomOAuth2LogoutSuccessHandler(
} this.applicationProperties,
} sessionRegistry())));
})
// Add existing Authorities from the database
.userInfoEndpoint(
userInfoEndpoint ->
userInfoEndpoint.userAuthoritiesMapper(
userAuthoritiesMapper())));
} }
} else { } else {
http.csrf(csrf -> csrf.disable()) http.csrf(csrf -> csrf.disable())
@ -225,13 +204,13 @@ public class SecurityConfiguration {
} }
private ClientRegistration oidcClientRegistration() { private ClientRegistration oidcClientRegistration() {
return ClientRegistrations.fromOidcIssuerLocation( OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
applicationProperties.getSecurity().getOAUTH2().getIssuer()) return ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
.registrationId("oidc") .registrationId("oidc")
.clientId(applicationProperties.getSecurity().getOAUTH2().getClientId()) .clientId(oauth.getClientId())
.clientSecret(applicationProperties.getSecurity().getOAUTH2().getClientSecret()) .clientSecret(oauth.getClientSecret())
.scope("openid", "profile", "email") .scope(oauth.getScopes())
.userNameAttributeName("email") .userNameAttributeName(oauth.getUseAsUsername())
.clientName("OIDC") .clientName("OIDC")
.build(); .build();
} }
@ -256,9 +235,14 @@ public class SecurityConfiguration {
// Add Authorities from database for existing user, if user is present. // Add Authorities from database for existing user, if user is present.
if (authority instanceof OAuth2UserAuthority oauth2Auth) { if (authority instanceof OAuth2UserAuthority oauth2Auth) {
String useAsUsername =
applicationProperties
.getSecurity()
.getOAUTH2()
.getUseAsUsername();
Optional<User> userOpt = Optional<User> userOpt =
userService.findByUsernameIgnoreCase( userService.findByUsernameIgnoreCase(
(String) oauth2Auth.getAttributes().get("email")); (String) oauth2Auth.getAttributes().get(useAsUsername));
if (userOpt.isPresent()) { if (userOpt.isPresent()) {
User user = userOpt.get(); User user = userOpt.get();
if (user != null) { if (user != null) {

View File

@ -8,6 +8,8 @@ import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
@ -18,6 +20,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Authority; import stirling.software.SPDF.model.Authority;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
@ -33,19 +36,19 @@ public class UserService implements UserServiceInterface {
@Autowired private PasswordEncoder passwordEncoder; @Autowired private PasswordEncoder passwordEncoder;
@Autowired private MessageSource messageSource;
// Handle OAUTH2 login and user auto creation. // Handle OAUTH2 login and user auto creation.
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) { public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) {
Optional<User> existUser = userRepository.findByUsernameIgnoreCase(username); if (!isUsernameValid(username)) {
if (existUser.isPresent()) { return false;
}
Optional<User> existingUser = userRepository.findByUsernameIgnoreCase(username);
if (existingUser.isPresent()) {
return true; return true;
} }
if (autoCreateUser) { if (autoCreateUser) {
User user = new User(); saveUser(username, AuthenticationType.OAUTH2);
user.setUsername(username);
user.setEnabled(true);
user.setFirstLogin(false);
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
userRepository.save(user);
return true; return true;
} }
return false; return false;
@ -111,9 +114,8 @@ public class UserService implements UserServiceInterface {
} }
public UserDetails loadUserByApiKey(String apiKey) { public UserDetails loadUserByApiKey(String apiKey) {
User userOptional = userRepository.findByApiKey(apiKey); User user = userRepository.findByApiKey(apiKey);
if (userOptional != null) { if (user != null) {
User user = userOptional;
// Convert your User entity to a UserDetails object with authorities // Convert your User entity to a UserDetails object with authorities
return new org.springframework.security.core.userdetails.User( return new org.springframework.security.core.userdetails.User(
user.getUsername(), user.getUsername(),
@ -125,35 +127,53 @@ public class UserService implements UserServiceInterface {
public boolean validateApiKeyForUser(String username, String apiKey) { public boolean validateApiKeyForUser(String username, String apiKey) {
Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username); Optional<User> userOpt = userRepository.findByUsernameIgnoreCase(username);
return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey); return userOpt.isPresent() && apiKey.equals(userOpt.get().getApiKey());
} }
public void saveUser(String username, String password) { public void saveUser(String username, AuthenticationType authenticationType)
throws IllegalArgumentException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
User user = new User();
user.setUsername(username);
user.setEnabled(true);
user.setFirstLogin(false);
user.addAuthority(new Authority(Role.USER.getRoleId(), user));
user.setAuthenticationType(authenticationType);
userRepository.save(user);
}
public void saveUser(String username, String password) throws IllegalArgumentException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
User user = new User(); User user = new User();
user.setUsername(username); user.setUsername(username);
user.setPassword(passwordEncoder.encode(password)); user.setPassword(passwordEncoder.encode(password));
user.setEnabled(true); user.setEnabled(true);
user.setAuthenticationType(AuthenticationType.WEB);
userRepository.save(user); userRepository.save(user);
} }
public void saveUser(String username, String password, String role, boolean firstLogin) { public void saveUser(String username, String password, String role, boolean firstLogin)
throws IllegalArgumentException {
if (!isUsernameValid(username)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
User user = new User(); User user = new User();
user.setUsername(username); user.setUsername(username);
user.setPassword(passwordEncoder.encode(password)); user.setPassword(passwordEncoder.encode(password));
user.addAuthority(new Authority(role, user)); user.addAuthority(new Authority(role, user));
user.setEnabled(true); user.setEnabled(true);
user.setAuthenticationType(AuthenticationType.WEB);
user.setFirstLogin(firstLogin); user.setFirstLogin(firstLogin);
userRepository.save(user); userRepository.save(user);
} }
public void saveUser(String username, String password, String role) { public void saveUser(String username, String password, String role)
User user = new User(); throws IllegalArgumentException {
user.setUsername(username); saveUser(username, password, role, false);
user.setPassword(passwordEncoder.encode(password));
user.addAuthority(new Authority(role, user));
user.setEnabled(true);
user.setFirstLogin(false);
userRepository.save(user);
} }
public void deleteUser(String username) { public void deleteUser(String username) {
@ -187,7 +207,7 @@ public class UserService implements UserServiceInterface {
Map<String, String> settingsMap = user.getSettings(); Map<String, String> settingsMap = user.getSettings();
if (settingsMap == null) { if (settingsMap == null) {
settingsMap = new HashMap<String, String>(); settingsMap = new HashMap<>();
} }
settingsMap.clear(); settingsMap.clear();
settingsMap.putAll(updates); settingsMap.putAll(updates);
@ -209,7 +229,10 @@ public class UserService implements UserServiceInterface {
return authorityRepository.findByUserId(user.getId()); return authorityRepository.findByUserId(user.getId());
} }
public void changeUsername(User user, String newUsername) { public void changeUsername(User user, String newUsername) throws IllegalArgumentException {
if (!isUsernameValid(newUsername)) {
throw new IllegalArgumentException(getInvalidUsernameMessage());
}
user.setUsername(newUsername); user.setUsername(newUsername);
userRepository.save(user); userRepository.save(user);
} }
@ -235,6 +258,30 @@ public class UserService implements UserServiceInterface {
} }
public boolean isUsernameValid(String username) { public boolean isUsernameValid(String username) {
return username.matches("[a-zA-Z0-9]+"); // Checks whether the simple username is formatted correctly
boolean isValidSimpleUsername =
username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$");
// Checks whether the email address is formatted correctly
boolean isValidEmail =
username.matches(
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
return isValidSimpleUsername || isValidEmail;
}
private String getInvalidUsernameMessage() {
return messageSource.getMessage(
"invalidUsernameMessage", null, LocaleContextHolder.getLocale());
}
public boolean hasPassword(String username) {
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
return user.isPresent() && user.get().hasPassword();
}
public boolean isAuthenticationTypeByUsername(
String username, AuthenticationType authenticationType) {
Optional<User> user = userRepository.findByUsernameIgnoreCase(username);
return user.isPresent()
&& authenticationType.name().equalsIgnoreCase(user.get().getAuthenticationType());
} }
} }

View File

@ -0,0 +1,49 @@
package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class CustomOAuth2AuthenticationFailureHandler
extends SimpleUrlAuthenticationFailureHandler {
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2AuthenticationFailureHandler.class);
@Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
if (exception instanceof OAuth2AuthenticationException) {
OAuth2Error error = ((OAuth2AuthenticationException) exception).getError();
String errorCode = error.getErrorCode();
if (error.getErrorCode().equals("Password must not be null")) {
errorCode = "userAlreadyExistsWeb";
}
logger.error("OAuth2 Authentication error: " + errorCode);
getRedirectStrategy()
.sendRedirect(request, response, "/logout?erroroauth=" + errorCode);
return;
} else if (exception instanceof LockedException) {
logger.error("Account locked: ", exception);
getRedirectStrategy().sendRedirect(request, response, "/logout?error=locked");
} else {
logger.error("Unhandled authentication exception", exception);
super.onAuthenticationFailure(request, response, exception);
}
}
}

View File

@ -0,0 +1,93 @@
package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.SavedRequest;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.utils.RequestUriUtils;
public class CustomOAuth2AuthenticationSuccessHandler
extends SavedRequestAwareAuthenticationSuccessHandler {
private LoginAttemptService loginAttemptService;
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2AuthenticationSuccessHandler.class);
private ApplicationProperties applicationProperties;
private UserService userService;
public CustomOAuth2AuthenticationSuccessHandler(
final LoginAttemptService loginAttemptService,
ApplicationProperties applicationProperties,
UserService userService) {
this.applicationProperties = applicationProperties;
this.userService = userService;
this.loginAttemptService = loginAttemptService;
}
@Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
// Get the saved request
HttpSession session = request.getSession(false);
SavedRequest savedRequest =
(session != null)
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
: null;
if (savedRequest != null
&& !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
// Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication);
} else {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
OAUTH2 oAuth = applicationProperties.getSecurity().getOAUTH2();
String username = oauthUser.getName();
if (loginAttemptService.isBlocked(username)) {
if (session != null) {
session.removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
}
throw new LockedException(
"Your account has been locked due to too many failed login attempts.");
}
if (userService.usernameExistsIgnoreCase(username)
&& userService.hasPassword(username)
&& !userService.isAuthenticationTypeByUsername(
username, AuthenticationType.OAUTH2)
&& oAuth.getAutoCreateUser()) {
response.sendRedirect(
request.getContextPath() + "/logout?oauth2AuthenticationErrorWeb=true");
return;
} else {
try {
userService.processOAuth2PostLogin(username, oAuth.getAutoCreateUser());
response.sendRedirect("/");
return;
} catch (IllegalArgumentException e) {
response.sendRedirect("/logout?invalidUsername=true");
return;
}
}
}
}
}

View File

@ -0,0 +1,86 @@
package stirling.software.SPDF.config.security.oauth2;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
public class CustomOAuth2LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private static final Logger logger =
LoggerFactory.getLogger(CustomOAuth2LogoutSuccessHandler.class);
private final SessionRegistry sessionRegistry;
private final ApplicationProperties applicationProperties;
public CustomOAuth2LogoutSuccessHandler(
ApplicationProperties applicationProperties, SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
this.applicationProperties = applicationProperties;
}
@Override
public void onLogoutSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String param = "logout=true";
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
String provider = oauth.getProvider() != null ? oauth.getProvider() : "";
if (request.getParameter("oauth2AuthenticationErrorWeb") != null) {
param = "erroroauth=oauth2AuthenticationErrorWeb";
} else if (request.getParameter("error") != null) {
param = "error=" + request.getParameter("error");
} else if (request.getParameter("erroroauth") != null) {
param = "erroroauth=" + request.getParameter("erroroauth");
} else if (request.getParameter("oauth2AutoCreateDisabled") != null) {
param = "error=oauth2AutoCreateDisabled";
}
HttpSession session = request.getSession(false);
if (session != null) {
String sessionId = session.getId();
sessionRegistry.removeSessionInformation(sessionId);
session.invalidate();
logger.debug("Session invalidated: " + sessionId);
}
switch (provider) {
case "keycloak":
String logoutUrl =
oauth.getIssuer()
+ "/protocol/openid-connect/logout"
+ "?client_id="
+ oauth.getClientId()
+ "&post_logout_redirect_uri="
+ response.encodeRedirectURL(
request.getScheme()
+ "://"
+ request.getHeader("host")
+ "/login?"
+ param);
logger.debug("Redirecting to Keycloak logout URL: " + logoutUrl);
response.sendRedirect(logoutUrl);
break;
case "google":
// Add Google specific logout URL if needed
default:
String redirectUrl = request.getContextPath() + "/login?" + param;
logger.debug("Redirecting to default logout URL: " + redirectUrl);
response.sendRedirect(redirectUrl);
break;
}
}
}

View File

@ -0,0 +1,73 @@
package stirling.software.SPDF.config.security.oauth2;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import stirling.software.SPDF.config.security.LoginAttemptService;
import stirling.software.SPDF.config.security.UserService;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.User;
public class CustomOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
private final OidcUserService delegate = new OidcUserService();
private UserService userService;
private LoginAttemptService loginAttemptService;
private ApplicationProperties applicationProperties;
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
public CustomOAuth2UserService(
ApplicationProperties applicationProperties,
UserService userService,
LoginAttemptService loginAttemptService) {
this.applicationProperties = applicationProperties;
this.userService = userService;
this.loginAttemptService = loginAttemptService;
}
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
String usernameAttribute =
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
try {
OidcUser user = delegate.loadUser(userRequest);
String username = user.getUserInfo().getClaimAsString(usernameAttribute);
Optional<User> duser = userService.findByUsernameIgnoreCase(username);
if (duser.isPresent()) {
if (loginAttemptService.isBlocked(username)) {
throw new LockedException(
"Your account has been locked due to too many failed login attempts.");
}
if (userService.hasPassword(username)) {
throw new IllegalArgumentException("Password must not be null");
}
}
// Return a new OidcUser with adjusted attributes
return new DefaultOidcUser(
user.getAuthorities(),
userRequest.getIdToken(),
user.getUserInfo(),
usernameAttribute);
} catch (java.lang.IllegalArgumentException e) {
logger.error("Error loading OIDC user: {}", e.getMessage());
throw new OAuth2AuthenticationException(new OAuth2Error(e.getMessage()), e);
} catch (Exception e) {
logger.error("Unexpected error loading OIDC user", e);
throw new OAuth2AuthenticationException("Unexpected error during authentication");
}
}
}

View File

@ -0,0 +1,57 @@
package stirling.software.SPDF.config.security.oauth2;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import stirling.software.SPDF.model.ApplicationProperties;
public class CustomOAuthUserService implements OAuth2UserService<OidcUserRequest, OidcUser> {
private static final Logger logger = LoggerFactory.getLogger(CustomOAuthUserService.class);
private final OidcUserService delegate = new OidcUserService();
private ApplicationProperties applicationProperties;
public CustomOAuthUserService(ApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
}
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
String usernameAttribute =
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername();
try {
OidcUser user = delegate.loadUser(userRequest);
Map<String, Object> attributes = new HashMap<>(user.getAttributes());
// Ensure the preferred username attribute is present
if (!attributes.containsKey(usernameAttribute)) {
attributes.put(usernameAttribute, attributes.getOrDefault("email", ""));
usernameAttribute = "email";
logger.info("Adjusted username attribute to use email");
}
// Return a new OidcUser with adjusted attributes
return new DefaultOidcUser(
user.getAuthorities(),
userRequest.getIdToken(),
user.getUserInfo(),
usernameAttribute);
} catch (java.lang.IllegalArgumentException e) {
throw new OAuth2AuthenticationException(
new OAuth2Error(e.getMessage()), e.getMessage(), e);
}
}
}

View File

@ -47,8 +47,11 @@ public class UserController {
model.addAttribute("error", "Username already exists"); model.addAttribute("error", "Username already exists");
return "register"; return "register";
} }
try {
userService.saveUser(requestModel.getUsername(), requestModel.getPassword()); userService.saveUser(requestModel.getUsername(), requestModel.getPassword());
} catch (IllegalArgumentException e) {
return "redirect:/login?messageType=invalidUsername";
}
return "redirect:/login?registered=true"; return "redirect:/login?registered=true";
} }
@ -92,7 +95,11 @@ public class UserController {
} }
if (newUsername != null && newUsername.length() > 0) { if (newUsername != null && newUsername.length() > 0) {
userService.changeUsername(user, newUsername); try {
userService.changeUsername(user, newUsername);
} catch (IllegalArgumentException e) {
return new RedirectView("/account?messageType=invalidUsername");
}
} }
// Logout using Spring's utility // Logout using Spring's utility

View File

@ -43,9 +43,52 @@ public class AccountWebController {
model.addAttribute("currentPage", "login"); model.addAttribute("currentPage", "login");
if (request.getParameter("error") != null) { String error = request.getParameter("error");
if (error != null) {
model.addAttribute("error", request.getParameter("error")); switch (error) {
case "badcredentials":
error = "login.invalid";
break;
case "locked":
error = "login.locked";
break;
case "oauth2AuthenticationError":
error = "userAlreadyExistsOAuthMessage";
break;
default:
break;
}
model.addAttribute("error", error);
}
String erroroauth = request.getParameter("erroroauth");
if (erroroauth != null) {
switch (erroroauth) {
case "oauth2AutoCreateDisabled":
erroroauth = "login.oauth2AutoCreateDisabled";
break;
case "invalidUsername":
erroroauth = "login.invalid";
break;
case "userAlreadyExistsWeb":
erroroauth = "userAlreadyExistsWebMessage";
break;
case "oauth2AuthenticationErrorWeb":
erroroauth = "login.oauth2InvalidUserType";
break;
case "invalid_token_response":
erroroauth = "login.oauth2InvalidTokenResponse";
default:
break;
}
model.addAttribute("erroroauth", erroroauth);
}
if (request.getParameter("messageType") != null) {
model.addAttribute("messageType", "changedCredsMessage");
} }
if (request.getParameter("logout") != null) { if (request.getParameter("logout") != null) {
@ -60,7 +103,8 @@ public class AccountWebController {
@PreAuthorize("hasRole('ROLE_ADMIN')") @PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/addUsers") @GetMapping("/addUsers")
public String showAddUserForm(Model model, Authentication authentication) { public String showAddUserForm(
HttpServletRequest request, Model model, Authentication authentication) {
List<User> allUsers = userRepository.findAll(); List<User> allUsers = userRepository.findAll();
Iterator<User> iterator = allUsers.iterator(); Iterator<User> iterator = allUsers.iterator();
Map<String, String> roleDetails = Role.getAllRoleDetails(); Map<String, String> roleDetails = Role.getAllRoleDetails();
@ -78,6 +122,52 @@ public class AccountWebController {
} }
} }
String messageType = request.getParameter("messageType");
String deleteMessage = null;
if (messageType != null) {
switch (messageType) {
case "deleteCurrentUser":
deleteMessage = "deleteCurrentUserMessage";
break;
case "deleteUsernameExists":
deleteMessage = "deleteUsernameExistsMessage";
break;
default:
break;
}
model.addAttribute("deleteMessage", deleteMessage);
String addMessage = null;
switch (messageType) {
case "usernameExists":
addMessage = "usernameExistsMessage";
break;
case "invalidUsername":
addMessage = "invalidUsernameMessage";
break;
default:
break;
}
model.addAttribute("addMessage", addMessage);
}
String changeMessage = null;
if (messageType != null) {
switch (messageType) {
case "userNotFound":
changeMessage = "userNotFoundMessage";
break;
case "downgradeCurrentUser":
changeMessage = "downgradeCurrentUserMessage";
break;
default:
break;
}
model.addAttribute("changeMessage", changeMessage);
}
model.addAttribute("users", allUsers); model.addAttribute("users", allUsers);
model.addAttribute("currentUsername", authentication.getName()); model.addAttribute("currentUsername", authentication.getName());
model.addAttribute("roleDetails", roleDetails); model.addAttribute("roleDetails", roleDetails);
@ -109,8 +199,9 @@ public class AccountWebController {
OAuth2User userDetails = (OAuth2User) principal; OAuth2User userDetails = (OAuth2User) principal;
// Retrieve username and other attributes // Retrieve username and other attributes
username = userDetails.getAttribute("email"); username =
userDetails.getAttribute(
applicationProperties.getSecurity().getOAUTH2().getUseAsUsername());
// Add oAuth2 Login attributes to the model // Add oAuth2 Login attributes to the model
model.addAttribute("oAuth2Login", true); model.addAttribute("oAuth2Login", true);
} }
@ -135,6 +226,30 @@ public class AccountWebController {
return "redirect:/error"; // Example redirection in case of error return "redirect:/error"; // Example redirection in case of error
} }
String messageType = request.getParameter("messageType");
if (messageType != null) {
switch (messageType) {
case "notAuthenticated":
messageType = "notAuthenticatedMessage";
break;
case "userNotFound":
messageType = "userNotFoundMessage";
break;
case "incorrectPassword":
messageType = "incorrectPasswordMessage";
break;
case "usernameExists":
messageType = "usernameExistsMessage";
break;
case "invalidUsername":
messageType = "invalidUsernameMessage";
break;
default:
break;
}
model.addAttribute("messageType", messageType);
}
// Add attributes to the model // Add attributes to the model
model.addAttribute("username", username); model.addAttribute("username", username);
model.addAttribute("role", user.get().getRolesAsString()); model.addAttribute("role", user.get().getRolesAsString());
@ -173,6 +288,28 @@ public class AccountWebController {
// Handle error appropriately // Handle error appropriately
return "redirect:/error"; // Example redirection in case of error return "redirect:/error"; // Example redirection in case of error
} }
String messageType = request.getParameter("messageType");
if (messageType != null) {
switch (messageType) {
case "notAuthenticated":
messageType = "notAuthenticatedMessage";
break;
case "userNotFound":
messageType = "userNotFoundMessage";
break;
case "incorrectPassword":
messageType = "incorrectPasswordMessage";
break;
case "usernameExists":
messageType = "usernameExistsMessage";
break;
default:
break;
}
model.addAttribute("messageType", messageType);
}
// Add attributes to the model // Add attributes to the model
model.addAttribute("username", username); model.addAttribute("username", username);
} }

View File

@ -1,6 +1,10 @@
package stirling.software.SPDF.model; package stirling.software.SPDF.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -221,6 +225,10 @@ public class ApplicationProperties {
private String clientId; private String clientId;
private String clientSecret; private String clientSecret;
private boolean autoCreateUser; private boolean autoCreateUser;
private String useAsUsername;
private String provider;
private Collection<String> scopes = new ArrayList<String>();
public boolean getEnabled() { public boolean getEnabled() {
return enabled; return enabled;
@ -262,6 +270,37 @@ public class ApplicationProperties {
this.autoCreateUser = autoCreateUser; this.autoCreateUser = autoCreateUser;
} }
public String getUseAsUsername() {
if (useAsUsername != null && useAsUsername.trim().length() > 0) {
return useAsUsername;
}
return "email";
}
public void setUseAsUsername(String useAsUsername) {
this.useAsUsername = useAsUsername;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public Collection<String> getScopes() {
return scopes;
}
public void setScopes(String scpoes) {
List<String> scopesList =
Arrays.stream(scpoes.split(","))
.map(String::trim)
.collect(Collectors.toList());
this.scopes.addAll(scopesList);
}
@Override @Override
public String toString() { public String toString() {
return "OAUTH2 [enabled=" return "OAUTH2 [enabled="
@ -274,6 +313,12 @@ public class ApplicationProperties {
+ (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL")
+ ", autoCreateUser=" + ", autoCreateUser="
+ autoCreateUser + autoCreateUser
+ ", useAsUsername="
+ useAsUsername
+ ", provider"
+ provider
+ ", scopes="
+ scopes
+ "]"; + "]";
} }
} }

View File

@ -5,7 +5,7 @@ public class AttemptCounter {
private long lastAttemptTime; private long lastAttemptTime;
public AttemptCounter() { public AttemptCounter() {
this.attemptCount = 1; this.attemptCount = 0;
this.lastAttemptTime = System.currentTimeMillis(); this.lastAttemptTime = System.currentTimeMillis();
} }
@ -18,11 +18,16 @@ public class AttemptCounter {
return attemptCount; return attemptCount;
} }
public long getlastAttemptTime() { public long getLastAttemptTime() {
return lastAttemptTime; return lastAttemptTime;
} }
public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) { public boolean shouldReset(long attemptIncrementTime) {
return System.currentTimeMillis() - lastAttemptTime > ATTEMPT_INCREMENT_TIME; return System.currentTimeMillis() - lastAttemptTime > attemptIncrementTime;
}
public void reset() {
this.attemptCount = 0;
this.lastAttemptTime = System.currentTimeMillis();
} }
} }

View File

@ -0,0 +1,6 @@
package stirling.software.SPDF.model;
public enum AuthenticationType {
WEB,
OAUTH2
}

View File

@ -47,6 +47,9 @@ public class User {
@Column(name = "roleName") @Column(name = "roleName")
private String roleName; private String roleName;
@Column(name = "authenticationtype")
private String authenticationType;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user") @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
private Set<Authority> authorities = new HashSet<>(); private Set<Authority> authorities = new HashSet<>();
@ -116,6 +119,14 @@ public class User {
this.enabled = enabled; this.enabled = enabled;
} }
public void setAuthenticationType(AuthenticationType authenticationType) {
this.authenticationType = authenticationType.toString().toLowerCase();
}
public String getAuthenticationType() {
return authenticationType;
}
public Set<Authority> getAuthorities() { public Set<Authority> getAuthorities() {
return authorities; return authorities;
} }
@ -137,4 +148,8 @@ public class User {
.map(Authority::getAuthority) .map(Authority::getAuthority)
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
} }
public boolean hasPassword() {
return this.password != null && !this.password.isEmpty();
}
} }

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي
downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي. downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -72,6 +74,7 @@ sponsor=Sponsor
info=Info info=Info
############### ###############
# Pipeline # # Pipeline #
############### ###############
@ -170,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -182,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=تغيير دور المستخدم adminUserSettings.changeUserRole=تغيير دور المستخدم
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -1056,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Потребителят не е автентикира
userNotFoundMessage=Потребителят не е намерен userNotFoundMessage=Потребителят не е намерен
incorrectPasswordMessage=Текущата парола е неправилна. incorrectPasswordMessage=Текущата парола е неправилна.
usernameExistsMessage=Новият потребител вече съществува. usernameExistsMessage=Новият потребител вече съществува.
invalidUsernameMessage=Невалидно потребителско име, потребителското име трябва да съдържа само букви и цифри. invalidUsernameMessage=Невалидно потребителско име, потребителското име може да съдържа само букви, цифри и следните специални знаци @._+- или трябва да е валиден имейл адрес.
deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител. deleteCurrentUserMessage=Не може да се изтрие вписания в момента потребител.
deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито. deleteUsernameExistsMessage=Потребителското име не съществува и не може да бъде изтрито.
downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител
downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан. downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Грешка error=Грешка
oops=Опаа! oops=Опаа!
help=Помощ help=Помощ
@ -73,7 +75,6 @@ info=Info
############### ###############
# Pipeline # # Pipeline #
############### ###############
@ -172,7 +173,7 @@ adminUserSettings.header=Настройки за администраторск
adminUserSettings.admin=Администратор adminUserSettings.admin=Администратор
adminUserSettings.user=Потребител adminUserSettings.user=Потребител
adminUserSettings.addUser=Добавяне на нов потребител adminUserSettings.addUser=Добавяне на нов потребител
adminUserSettings.usernameInfo=Потребителското име трябва да съдържа само букви и цифри, без интервали или специални знаци. adminUserSettings.usernameInfo=Потребителското име може да съдържа само букви, цифри и следните специални символи @._+- или трябва да е валиден имейл адрес.
adminUserSettings.roles=Роли adminUserSettings.roles=Роли
adminUserSettings.role=Роля adminUserSettings.role=Роля
adminUserSettings.actions=Действия adminUserSettings.actions=Действия
@ -184,6 +185,7 @@ adminUserSettings.internalApiUser=Вътрешен API потребител
adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане
adminUserSettings.submit=Съхранете потребителя adminUserSettings.submit=Съхранете потребителя
adminUserSettings.changeUserRole=Промяна на ролята на потребителя adminUserSettings.changeUserRole=Промяна на ролята на потребителя
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -805,7 +807,6 @@ multiTool.title=PDF Мулти инструмент
multiTool.header=PDF Мулти инструмент multiTool.header=PDF Мулти инструмент
multiTool.uploadPrompts=File Name multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Преглед на PDF viewPdf.title=Преглед на PDF
viewPdf.header=Преглед на PDF viewPdf.header=Преглед на PDF
@ -1059,7 +1060,7 @@ licenses.version=Версия
licenses.license=Лиценз licenses.license=Лиценз
# error #error
error.sorry=Извинете за проблема! error.sorry=Извинете за проблема!
error.needHelp=Нуждаете се от помощ / Открихте проблем? error.needHelp=Нуждаете се от помощ / Открихте проблем?
error.contactTip=Ако все още имате проблеми, не се колебайте да се свържете с нас за помощ. Можете да изпратите запитване на нашата страница в GitHub или да се свържете с нас чрез Discord: error.contactTip=Ако все още имате проблеми, не се колебайте да се свържете с нас за помощ. Можете да изпратите запитване на нашата страница в GitHub или да се свържете с нас чрез Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual
downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual. downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Usuari Admin Opcions Control
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=Usuari adminUserSettings.user=Usuari
adminUserSettings.addUser=Afegir Usuari adminUserSettings.addUser=Afegir Usuari
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rols adminUserSettings.roles=Rols
adminUserSettings.role=Rol adminUserSettings.role=Rol
adminUserSettings.actions=Accions adminUserSettings.actions=Accions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Desar Usuari adminUserSettings.submit=Desar Usuari
adminUserSettings.changeUserRole=Canvia el rol de l'usuari adminUserSettings.changeUserRole=Canvia el rol de l'usuari
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=PDF Multi Tool multiTool.title=PDF Multi Tool
multiTool.header=PDF Multi Tool multiTool.header=PDF Multi Tool
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Benutzer nicht authentifiziert.
userNotFoundMessage=Benutzer nicht gefunden. userNotFoundMessage=Benutzer nicht gefunden.
incorrectPasswordMessage=Das Passwort ist falsch. incorrectPasswordMessage=Das Passwort ist falsch.
usernameExistsMessage=Neuer Benutzername existiert bereits. usernameExistsMessage=Neuer Benutzername existiert bereits.
invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben und Zahlen enthalten. invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden. deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden.
deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden. deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden.
downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden
downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt. downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Fehler error=Fehler
oops=Hoppla! oops=Hoppla!
help=Hilfe help=Hilfe
@ -171,7 +173,7 @@ adminUserSettings.header=Administrator-Benutzerkontrolle
adminUserSettings.admin=Administrator adminUserSettings.admin=Administrator
adminUserSettings.user=Benutzer adminUserSettings.user=Benutzer
adminUserSettings.addUser=Neuen Benutzer hinzufügen adminUserSettings.addUser=Neuen Benutzer hinzufügen
adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben und Zahlen enthalten, keine Leerzeichen oder Sonderzeichen. adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben, Zahlen und die folgenden Sonderzeichen @._+- enthalten oder muss eine gültige E-Mail-Adresse sein.
adminUserSettings.roles=Rollen adminUserSettings.roles=Rollen
adminUserSettings.role=Rolle adminUserSettings.role=Rolle
adminUserSettings.actions=Aktion adminUserSettings.actions=Aktion
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Interner API-Benutzer
adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern
adminUserSettings.submit=Benutzer speichern adminUserSettings.submit=Benutzer speichern
adminUserSettings.changeUserRole=Benutzerrolle ändern adminUserSettings.changeUserRole=Benutzerrolle ändern
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
#multiTool #multiTool
multiTool.title=PDF-Multitool multiTool.title=PDF-Multitool
multiTool.header=PDF-Multitool multiTool.header=PDF-Multitool
multiTool.uploadPrompts=Bitte PDF hochladen multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=PDF anzeigen viewPdf.title=PDF anzeigen
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=Lizenz licenses.license=Lizenz
# error #error
error.sorry=Entschuldigung für das Problem! error.sorry=Entschuldigung für das Problem!
error.needHelp=Brauchst du Hilfe / Ein Problem gefunden? error.needHelp=Brauchst du Hilfe / Ein Problem gefunden?
error.contactTip=Wenn du weiterhin Probleme hast, zögere nicht, uns um Hilfe zu bitten. Du kannst ein Ticket auf unserer GitHub-Seite einreichen oder uns über Discord kontaktieren: error.contactTip=Wenn du weiterhin Probleme hast, zögere nicht, uns um Hilfe zu bitten. Du kannst ein Ticket auf unserer GitHub-Seite einreichen oder uns über Discord kontaktieren:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Ο χρήστης δεν έχει αυθεντικοπο
userNotFoundMessage=Ο χρήστης δεν βρέθηκε. userNotFoundMessage=Ο χρήστης δεν βρέθηκε.
incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος. incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος.
usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη. usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη.
invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, το όνομα χρήστη πρέπει να περιέχει μόνο αλφαβητικούς χαρακτήρες και αριθμούς. invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, όνομα χρήστη μπορεί να περιέχει μόνο γράμματα, αριθμούς και τους ακόλουθους ειδικούς χαρακτήρες @._+- ή πρέπει να είναι έγκυρη διεύθυνση email.
deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη. deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη.
deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί. deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί.
downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη
downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται. downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Σφάλμα error=Σφάλμα
oops=Ωχ! oops=Ωχ!
help=Βοήθεια help=Βοήθεια
@ -171,7 +173,7 @@ adminUserSettings.header=Ρυθμίσεις ελέγχου Διαχειριστ
adminUserSettings.admin=Διαχειριστής adminUserSettings.admin=Διαχειριστής
adminUserSettings.user=Χρήστης adminUserSettings.user=Χρήστης
adminUserSettings.addUser=Προσθήκη νέου Χρήστη adminUserSettings.addUser=Προσθήκη νέου Χρήστη
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Ρόλοι adminUserSettings.roles=Ρόλοι
adminUserSettings.role=Ρόλος adminUserSettings.role=Ρόλος
adminUserSettings.actions=Ενέργειες adminUserSettings.actions=Ενέργειες
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Εσωτερικός API χρήστης
adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση
adminUserSettings.submit=Αποθήκευση Χρήστη adminUserSettings.submit=Αποθήκευση Χρήστη
adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1)
#multiTool #multiTool
multiTool.title=PDF Πολυεργαλείο multiTool.title=PDF Πολυεργαλείο
multiTool.header=PDF Πολυεργαλείο multiTool.header=PDF Πολυεργαλείο
multiTool.uploadPrompts=Ανεβάστε το PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Προβολή PDF viewPdf.title=Προβολή PDF
@ -1057,7 +1060,7 @@ licenses.version=Εκδοχή
licenses.license=Άδεια licenses.license=Άδεια
# error #error
error.sorry=Συγγνώμη για το ζήτημα! error.sorry=Συγγνώμη για το ζήτημα!
error.needHelp=Χρειάζεστε βοήθεια / Βρήκατε πρόβλημα; error.needHelp=Χρειάζεστε βοήθεια / Βρήκατε πρόβλημα;
error.contactTip=Εάν εξακολουθείτε να αντιμετωπίζετε προβλήματα, μη διστάσετε να επικοινωνήσετε μαζί μας για βοήθεια. Μπορείτε να υποβάλετε ένα ticket στη σελίδα μας στο GitHub ή να επικοινωνήσετε μαζί μας μέσω του Discord: error.contactTip=Εάν εξακολουθείτε να αντιμετωπίζετε προβλήματα, μη διστάσετε να επικοινωνήσετε μαζί μας για βοήθεια. Μπορείτε να υποβάλετε ένα ticket στη σελίδα μας στο GitHub ή να επικοινωνήσετε μαζί μας μέσω του Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Cannot downgrade current user's role downgradeCurrentUserMessage=Cannot downgrade current user's role
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown. downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Change User's Role adminUserSettings.changeUserRole=Change User's Role
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Cannot downgrade current user's role downgradeCurrentUserMessage=Cannot downgrade current user's role
downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown. downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -72,6 +74,7 @@ sponsor=Sponsor
info=Info info=Info
############### ###############
# Pipeline # # Pipeline #
############### ###############
@ -170,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -182,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Change User's Role adminUserSettings.changeUserRole=Change User's Role
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -1056,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Usuario no autentificado.
userNotFoundMessage=Usuario no encontrado. userNotFoundMessage=Usuario no encontrado.
incorrectPasswordMessage=La contraseña actual no es correcta. incorrectPasswordMessage=La contraseña actual no es correcta.
usernameExistsMessage=El nuevo nombre de usuario está en uso. usernameExistsMessage=El nuevo nombre de usuario está en uso.
invalidUsernameMessage=Nombre de usuario no válido, El nombre de ususario debe contener únicamente números y caracteres alfabéticos. invalidUsernameMessage=Nombre de usuario no válido, el nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso. deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso.
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse. deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual
downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará. downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Ups! oops=Ups!
help=Help help=Help
@ -73,9 +75,6 @@ info=Info
############### ###############
# Pipeline # # Pipeline #
############### ###############
@ -174,7 +173,7 @@ adminUserSettings.header=Configuración de control de usuario administrador
adminUserSettings.admin=Administrador adminUserSettings.admin=Administrador
adminUserSettings.user=Usuario adminUserSettings.user=Usuario
adminUserSettings.addUser=Añadir Nuevo Usuario adminUserSettings.addUser=Añadir Nuevo Usuario
adminUserSettings.usernameInfo=El nombrede usuario debe contener únicamente letras y números, no espacios ni caracteres especiales. adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Rol adminUserSettings.role=Rol
adminUserSettings.actions=Acciones adminUserSettings.actions=Acciones
@ -186,6 +185,7 @@ adminUserSettings.internalApiUser=Usuario interno de API
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
adminUserSettings.submit=Guardar Usuario adminUserSettings.submit=Guardar Usuario
adminUserSettings.changeUserRole=Cambiar rol de usuario adminUserSettings.changeUserRole=Cambiar rol de usuario
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -805,7 +805,7 @@ pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
#multiTool #multiTool
multiTool.title=Multi-herramienta PDF multiTool.title=Multi-herramienta PDF
multiTool.header=Multi-herramienta PDF multiTool.header=Multi-herramienta PDF
multiTool.uploadPrompts=Por favor, cargue PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Ver PDF viewPdf.title=Ver PDF
@ -1060,7 +1060,7 @@ licenses.version=Versión
licenses.license=Licencia licenses.license=Licencia
# error #error
error.sorry=¡Perdón por el fallo! error.sorry=¡Perdón por el fallo!
error.needHelp=Necesita ayuda / Encontró un fallo? error.needHelp=Necesita ayuda / Encontró un fallo?
error.contactTip=Si sigue experimentando errores, no dude en contactarnos para solicitar soporte. Puede enviarnos un ticket en la página de GitHub o contactarnos mediante Discord: error.contactTip=Si sigue experimentando errores, no dude en contactarnos para solicitar soporte. Puede enviarnos un ticket en la página de GitHub o contactarnos mediante Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi
downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko. downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin Erabiltzailearen Ezarpenen Kontrolak
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=Erabiltzaile adminUserSettings.user=Erabiltzaile
adminUserSettings.addUser=Erabiltzaile berria adminUserSettings.addUser=Erabiltzaile berria
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rolak adminUserSettings.roles=Rolak
adminUserSettings.role=Rol adminUserSettings.role=Rol
adminUserSettings.actions=Ekintzak adminUserSettings.actions=Ekintzak
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Gorde Erabiltzailea adminUserSettings.submit=Gorde Erabiltzailea
adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=PDF erabilera anitzeko tresna multiTool.title=PDF erabilera anitzeko tresna
multiTool.header=PDF erabilera anitzeko tresna multiTool.header=PDF erabilera anitzeko tresna
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Utilisateur non authentifié.
userNotFoundMessage=Utilisateur non trouvé. userNotFoundMessage=Utilisateur non trouvé.
incorrectPasswordMessage=Le mot de passe actuel est incorrect. incorrectPasswordMessage=Le mot de passe actuel est incorrect.
usernameExistsMessage=Le nouveau nom dutilisateur existe déjà. usernameExistsMessage=Le nouveau nom dutilisateur existe déjà.
invalidUsernameMessage=Nom dutilisateur invalide, le nom dutilisateur ne peut contenir que des chiffres et des lettres. invalidUsernameMessage=Nom dutilisateur invalide, le nom dutilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
deleteCurrentUserMessage=Impossible de supprimer lutilisateur actuellement connecté. deleteCurrentUserMessage=Impossible de supprimer lutilisateur actuellement connecté.
deleteUsernameExistsMessage=Le nom dutilisateur nexiste pas et ne peut pas être supprimé. deleteUsernameExistsMessage=Le nom dutilisateur nexiste pas et ne peut pas être supprimé.
downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel
downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché. downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Erreur error=Erreur
oops=Oups ! oops=Oups !
help=Aide help=Aide
@ -72,6 +74,7 @@ sponsor=Sponsor
info=Info info=Info
############### ###############
# Pipeline # # Pipeline #
############### ###############
@ -170,7 +173,7 @@ adminUserSettings.header=Administration des paramètres des utilisateurs
adminUserSettings.admin=Administateur adminUserSettings.admin=Administateur
adminUserSettings.user=Utilisateur adminUserSettings.user=Utilisateur
adminUserSettings.addUser=Ajouter un utilisateur adminUserSettings.addUser=Ajouter un utilisateur
adminUserSettings.usernameInfo=Le nom dutilisateur ne doit contenir que des lettres et des chiffres, sans espaces ni caractères spéciaux. adminUserSettings.usernameInfo=Le nom d'utilisateur ne peut contenir que des lettres, des chiffres et les caractères spéciaux suivants @._+- ou doit être une adresse e-mail valide.
adminUserSettings.roles=Rôles adminUserSettings.roles=Rôles
adminUserSettings.role=Rôle adminUserSettings.role=Rôle
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -182,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Forcer lutilisateur à changer son nom dutilisateur/mot de passe lors de la connexion adminUserSettings.forceChange=Forcer lutilisateur à changer son nom dutilisateur/mot de passe lors de la connexion
adminUserSettings.submit=Ajouter adminUserSettings.submit=Ajouter
adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -801,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=Outil multifonction PDF multiTool.title=Outil multifonction PDF
multiTool.header=Outil multifonction PDF multiTool.header=Outil multifonction PDF
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Visualiser un PDF viewPdf.title=Visualiser un PDF
@ -1056,7 +1060,7 @@ licenses.version=Version
licenses.license=Licence licenses.license=Licence
# error #error
error.sorry=Désolé pour ce problème ! error.sorry=Désolé pour ce problème !
error.needHelp=Besoin daide / Vous avez trouvé un problème ? error.needHelp=Besoin daide / Vous avez trouvé un problème ?
error.contactTip=Si vous avez encore des problèmes, nhésitez pas à nous contacter pour obtenir de laide. Vous pouvez soumettre un ticket sur notre page GitHub ou nous contacter via Discord : error.contactTip=Si vous avez encore des problèmes, nhésitez pas à nous contacter pour obtenir de laide. Vous pouvez soumettre un ticket sur notre page GitHub ou nous contacter via Discord :

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=उपयोगकर्ता प्रमाणित
userNotFoundMessage=उपयोगकर्ता नहीं मिला। userNotFoundMessage=उपयोगकर्ता नहीं मिला।
incorrectPasswordMessage=वर्तमान पासवर्ड गलत है। incorrectPasswordMessage=वर्तमान पासवर्ड गलत है।
usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है। usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है।
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता
downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा। downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा।
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=व्यवस्थापक उपयोगकर्
adminUserSettings.admin=व्यवस्थापक adminUserSettings.admin=व्यवस्थापक
adminUserSettings.user=उपयोगकर्ता adminUserSettings.user=उपयोगकर्ता
adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=रोल्स adminUserSettings.roles=रोल्स
adminUserSettings.role=रोल adminUserSettings.role=रोल
adminUserSettings.actions=क्रियाएँ adminUserSettings.actions=क्रियाएँ
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें
adminUserSettings.submit=उपयोगकर्ता को सहेजें adminUserSettings.submit=उपयोगकर्ता को सहेजें
adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=पीडीएफ मल्टी टूल multiTool.title=पीडीएफ मल्टी टूल
multiTool.header=पीडीएफ मल्टी टूल multiTool.header=पीडीएफ मल्टी टूल
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=पीडीएफ देखें viewPdf.title=पीडीएफ देखें
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Felhasználó nincs hitelesítve.
userNotFoundMessage=A felhasználó nem található. userNotFoundMessage=A felhasználó nem található.
incorrectPasswordMessage=A jelenlegi jelszó helytelen. incorrectPasswordMessage=A jelenlegi jelszó helytelen.
usernameExistsMessage=Az új felhasználónév már létezik. usernameExistsMessage=Az új felhasználónév már létezik.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni
downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg. downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Adminisztrátori Felhasználói Vezérlési Beállítá
adminUserSettings.admin=Adminisztrátor adminUserSettings.admin=Adminisztrátor
adminUserSettings.user=Felhasználó adminUserSettings.user=Felhasználó
adminUserSettings.addUser=Új felhasználó hozzáadása adminUserSettings.addUser=Új felhasználó hozzáadása
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Szerepek adminUserSettings.roles=Szerepek
adminUserSettings.role=Szerep adminUserSettings.role=Szerep
adminUserSettings.actions=Műveletek adminUserSettings.actions=Műveletek
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor
adminUserSettings.submit=Felhasználó mentése adminUserSettings.submit=Felhasználó mentése
adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=PDF többfunkciós eszköz multiTool.title=PDF többfunkciós eszköz
multiTool.header=PDF többfunkciós eszköz multiTool.header=PDF többfunkciós eszköz
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=PDF megtekintése viewPdf.title=PDF megtekintése
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Pengguna tidak ter-autentikasi.
userNotFoundMessage=Pengguna tidak ditemukan. userNotFoundMessage=Pengguna tidak ditemukan.
incorrectPasswordMessage=Kata sandi saat ini salah. incorrectPasswordMessage=Kata sandi saat ini salah.
usernameExistsMessage=Nama pengguna baru sudah ada. usernameExistsMessage=Nama pengguna baru sudah ada.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini
downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan. downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Pengaturan Kontrol Admin
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=Pengguna adminUserSettings.user=Pengguna
adminUserSettings.addUser=Tambahkan Pengguna Baru adminUserSettings.addUser=Tambahkan Pengguna Baru
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Peran adminUserSettings.roles=Peran
adminUserSettings.role=Peran adminUserSettings.role=Peran
adminUserSettings.actions=Tindakan adminUserSettings.actions=Tindakan
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk
adminUserSettings.submit=Simpan Pengguna adminUserSettings.submit=Simpan Pengguna
adminUserSettings.changeUserRole=Ubah Peran Pengguna adminUserSettings.changeUserRole=Ubah Peran Pengguna
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=Alat Multi PDF multiTool.title=Alat Multi PDF
multiTool.header=Alat Multi PDF multiTool.header=Alat Multi PDF
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Lihat PDF viewPdf.title=Lihat PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Utente non autenticato.
userNotFoundMessage=Utente non trovato. userNotFoundMessage=Utente non trovato.
incorrectPasswordMessage=La password attuale non è corretta. incorrectPasswordMessage=La password attuale non è corretta.
usernameExistsMessage=Il nuovo nome utente esiste già. usernameExistsMessage=Il nuovo nome utente esiste già.
invalidUsernameMessage=Nome utente non valido, il nome utente deve contenere solo caratteri alfabetici e numeri. invalidUsernameMessage=Nome utente non valido, il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- o deve essere un indirizzo email valido.
deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso. deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso.
deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato. deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato.
downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente
downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato. downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Errore error=Errore
oops=Oops! oops=Oops!
help=Aiuto help=Aiuto
@ -171,7 +173,7 @@ adminUserSettings.header=Impostazioni di controllo utente amministratore
adminUserSettings.admin=Amministratore adminUserSettings.admin=Amministratore
adminUserSettings.user=Utente adminUserSettings.user=Utente
adminUserSettings.addUser=Aggiungi un nuovo Utente adminUserSettings.addUser=Aggiungi un nuovo Utente
adminUserSettings.usernameInfo=Il nome utente deve contenere solo lettere e numeri, senza spazi o caratteri speciali. adminUserSettings.usernameInfo=Il nome utente può contenere solo lettere, numeri e i seguenti caratteri speciali @._+- oppure deve essere un indirizzo email valido.
adminUserSettings.roles=Ruoli adminUserSettings.roles=Ruoli
adminUserSettings.role=Ruolo adminUserSettings.role=Ruolo
adminUserSettings.actions=Azioni adminUserSettings.actions=Azioni
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=API utente interna
adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso
adminUserSettings.submit=Salva utente adminUserSettings.submit=Salva utente
adminUserSettings.changeUserRole=Cambia il ruolo dell'utente adminUserSettings.changeUserRole=Cambia il ruolo dell'utente
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)
#multiTool #multiTool
multiTool.title=Multifunzione PDF multiTool.title=Multifunzione PDF
multiTool.header=Multifunzione PDF multiTool.header=Multifunzione PDF
multiTool.uploadPrompts=Caricare il PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Visualizza PDF viewPdf.title=Visualizza PDF
@ -1057,7 +1060,7 @@ licenses.version=Versione
licenses.license=Licenza licenses.license=Licenza
# error #error
error.sorry=Ci scusiamo per il problema! error.sorry=Ci scusiamo per il problema!
error.needHelp=Hai bisogno di aiuto / trovato un problema? error.needHelp=Hai bisogno di aiuto / trovato un problema?
error.contactTip=Se i problemi persistono, non esitare a contattarci per chiedere aiuto. Puoi inviare un ticket sulla nostra pagina GitHub o contattarci tramite Discord: error.contactTip=Se i problemi persistono, non esitare a contattarci per chiedere aiuto. Puoi inviare un ticket sulla nostra pagina GitHub o contattarci tramite Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=ユーザーが認証されていません。
userNotFoundMessage=ユーザーが見つかりません。 userNotFoundMessage=ユーザーが見つかりません。
incorrectPasswordMessage=現在のパスワードが正しくありません。 incorrectPasswordMessage=現在のパスワードが正しくありません。
usernameExistsMessage=新しいユーザー名はすでに存在します。 usernameExistsMessage=新しいユーザー名はすでに存在します。
invalidUsernameMessage=ユーザー名が無効です。ユーザー名にはアルファベットと数字のみを使用してください invalidUsernameMessage=ユーザー名が無効です。ユーザー名には文字、数字、およびそれに続く特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります
deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。 deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。
deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。 deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。
downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません
downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。 downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=エラー error=エラー
oops=おっと! oops=おっと!
help=ヘルプ help=ヘルプ
@ -171,7 +173,7 @@ adminUserSettings.header=管理者ユーザー制御設定
adminUserSettings.admin=管理者 adminUserSettings.admin=管理者
adminUserSettings.user=ユーザー adminUserSettings.user=ユーザー
adminUserSettings.addUser=新しいユーザを追加 adminUserSettings.addUser=新しいユーザを追加
adminUserSettings.usernameInfo=ユーザー名には文字と数字のみが使用でき、スペースや特殊文字は使用できません adminUserSettings.usernameInfo=ユーザー名には、文字、数字、および次の特殊文字 @._+- のみを含めることができます。または、有効な電子メール アドレスである必要があります
adminUserSettings.roles=役割 adminUserSettings.roles=役割
adminUserSettings.role=役割 adminUserSettings.role=役割
adminUserSettings.actions=アクション adminUserSettings.actions=アクション
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=内部APIユーザー
adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する
adminUserSettings.submit=ユーザーの保存 adminUserSettings.submit=ユーザーの保存
adminUserSettings.changeUserRole=ユーザーの役割を変更する adminUserSettings.changeUserRole=ユーザーの役割を変更する
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1)
#multiTool #multiTool
multiTool.title=PDFマルチツール multiTool.title=PDFマルチツール
multiTool.header=PDFマルチツール multiTool.header=PDFマルチツール
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=PDFを表示 viewPdf.title=PDFを表示
@ -1057,7 +1060,7 @@ licenses.version=バージョン
licenses.license=ライセンス licenses.license=ライセンス
# error #error
error.sorry=問題が発生したことをお詫び申し上げます! error.sorry=問題が発生したことをお詫び申し上げます!
error.needHelp=助けが必要/問題が見つかりましたか? error.needHelp=助けが必要/問題が見つかりましたか?
error.contactTip=まだ問題が解決していない場合は、お手数ですが、GitHubページでチケットを提出するか、Discordで私たちに連絡してください error.contactTip=まだ問題が解決していない場合は、お手数ですが、GitHubページでチケットを提出するか、Discordで私たちに連絡してください

View File

@ -54,13 +54,13 @@ notAuthenticatedMessage=사용자가 인증되지 않았습니다.
userNotFoundMessage=사용자를 찾을 수 없습니다. userNotFoundMessage=사용자를 찾을 수 없습니다.
incorrectPasswordMessage=현재 비밀번호가 틀립니다. incorrectPasswordMessage=현재 비밀번호가 틀립니다.
usernameExistsMessage=새 사용자명이 이미 존재합니다. usernameExistsMessage=새 사용자명이 이미 존재합니다.
invalidUsernameMessage=사용자 이름이 잘못되었습니다. 사용자 이름에는 알파벳 문자와 숫자만 포함되어야 합니다. invalidUsernameMessage=잘못된 사용자 이름입니다. 사용자 이름에는 문자, 숫자 및 다음 특수 문자(@._+-)만 포함할 수 있거나 유효한 이메일 주소여야 합니다.
deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다. deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다.
deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다. deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다.
info=Info
downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다 downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다
downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다. downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=오류 error=오류
oops=어머나! oops=어머나!
help=도움말 help=도움말
@ -71,6 +71,7 @@ visitGithub=GitHub 저장소 방문하기
donate=기부하기 donate=기부하기
color=색상 color=색상
sponsor=스폰서 sponsor=스폰서
info=Info
@ -172,7 +173,7 @@ adminUserSettings.header=사용자 관리
adminUserSettings.admin=관리자 adminUserSettings.admin=관리자
adminUserSettings.user=사용자 adminUserSettings.user=사용자
adminUserSettings.addUser=새 사용자 추가 adminUserSettings.addUser=새 사용자 추가
adminUserSettings.usernameInfo=사용자 이름은 문자와 숫자만 포함해야 하며 공백이나 특수 문자는 포함할 수 없습니다. adminUserSettings.usernameInfo=사용자 이름은 문자, 숫자, 특수 문자 @._+-만 포함할 수 있으며 유효한 이메일 주소여야 합니다.
adminUserSettings.roles=역할 adminUserSettings.roles=역할
adminUserSettings.role=역할 adminUserSettings.role=역할
adminUserSettings.actions=동작 adminUserSettings.actions=동작
@ -184,6 +185,7 @@ adminUserSettings.internalApiUser=내부 API 사용자
adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제 adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제
adminUserSettings.submit=사용자 저장 adminUserSettings.submit=사용자 저장
adminUserSettings.changeUserRole=사용자의 역할 변경 adminUserSettings.changeUserRole=사용자의 역할 변경
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -803,7 +805,7 @@ pdfOrganiser.placeholder=(예: 1,3,2 또는 4-8,2,10-12 또는 2n-1)
#multiTool #multiTool
multiTool.title=PDF 멀티툴 multiTool.title=PDF 멀티툴
multiTool.header=PDF 멀티툴 multiTool.header=PDF 멀티툴
multiTool.uploadPrompts=PDF를 업로드하십시오 multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=PDF 뷰어 viewPdf.title=PDF 뷰어
@ -1058,7 +1060,7 @@ licenses.version=버전
licenses.license=라이센스 licenses.license=라이센스
# error #error
error.sorry=문제를 끼친 점 죄송합니다! error.sorry=문제를 끼친 점 죄송합니다!
error.needHelp=도움이 필요하신가요 / 문제가 있으신가요? error.needHelp=도움이 필요하신가요 / 문제가 있으신가요?
error.contactTip=여전히 문제가 해결되지 않는다면 망설이지 마시고 도움을 요청하십시오. GitHub 페이지에서 티켓을 제출하거나 Discord를 통해 우리에게 연락하실 수 있습니다: error.contactTip=여전히 문제가 해결되지 않는다면 망설이지 마시고 도움을 요청하십시오. GitHub 페이지에서 티켓을 제출하거나 Discord를 통해 우리에게 연락하실 수 있습니다:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Gebruiker niet ingelogd.
userNotFoundMessage=Gebruiker niet gevonden. userNotFoundMessage=Gebruiker niet gevonden.
incorrectPasswordMessage=Huidige wachtwoord is onjuist. incorrectPasswordMessage=Huidige wachtwoord is onjuist.
usernameExistsMessage=Nieuwe gebruikersnaam bestaat al. usernameExistsMessage=Nieuwe gebruikersnaam bestaat al.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden
downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven. downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Beheer gebruikers
adminUserSettings.admin=Beheerder adminUserSettings.admin=Beheerder
adminUserSettings.user=Gebruiker adminUserSettings.user=Gebruiker
adminUserSettings.addUser=Voeg nieuwe gebruiker toe adminUserSettings.addUser=Voeg nieuwe gebruiker toe
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Rollen adminUserSettings.roles=Rollen
adminUserSettings.role=Rol adminUserSettings.role=Rol
adminUserSettings.actions=Acties adminUserSettings.actions=Acties
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen
adminUserSettings.submit=Gebruiker opslaan adminUserSettings.submit=Gebruiker opslaan
adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=PDF Multitool multiTool.title=PDF Multitool
multiTool.header=PDF Multitool multiTool.header=PDF Multitool
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=PDF bekijken viewPdf.title=PDF bekijken
@ -1057,7 +1060,7 @@ licenses.version=Versie
licenses.license=Licentie licenses.license=Licentie
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika
downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony. downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Zmień rolę użytkownika adminUserSettings.changeUserRole=Zmień rolę użytkownika
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=Multi narzędzie PDF multiTool.title=Multi narzędzie PDF
multiTool.header=Multi narzędzie PDF multiTool.header=Multi narzędzie PDF
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado. downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Alterar Função de Usuário adminUserSettings.changeUserRole=Alterar Função de Usuário
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=Multiferramenta de PDF multiTool.title=Multiferramenta de PDF
multiTool.header=Multiferramenta de PDF multiTool.header=Multiferramenta de PDF
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Utilizador não autenticado.
userNotFoundMessage=Utilizador inexistente. userNotFoundMessage=Utilizador inexistente.
incorrectPasswordMessage=Senha incorreta. incorrectPasswordMessage=Senha incorreta.
usernameExistsMessage=Esse utilizador já existe. usernameExistsMessage=Esse utilizador já existe.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Não é possível fazer downgrade da função do utilizador atual downgradeCurrentUserMessage=Não é possível fazer downgrade da função do utilizador atual
downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do utilizador atual. Portanto, o utilizador atual não será mostrado. downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do utilizador atual. Portanto, o utilizador atual não será mostrado.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Alterar usuário adminUserSettings.changeUserRole=Alterar usuário
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=Multiferramenta de PDF multiTool.title=Multiferramenta de PDF
multiTool.header=Multiferramenta de PDF multiTool.header=Multiferramenta de PDF
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Versão
licenses.license=Licença licenses.license=Licença
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Rolul utilizatorului curent nu poate fi retrogradat downgradeCurrentUserMessage=Rolul utilizatorului curent nu poate fi retrogradat
downgradeCurrentUserLongMessage=Rolul utilizatorului curent nu poate fi retrogradat. Prin urmare, utilizatorul curent nu va fi afișat. downgradeCurrentUserLongMessage=Rolul utilizatorului curent nu poate fi retrogradat. Prin urmare, utilizatorul curent nu va fi afișat.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Schimbați rolul utilizatorului adminUserSettings.changeUserRole=Schimbați rolul utilizatorului
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=Instrument PDF multiplu multiTool.title=Instrument PDF multiplu
multiTool.header=Instrument PDF multiplu multiTool.header=Instrument PDF multiplu
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,12 +54,13 @@ notAuthenticatedMessage=Пользователь не прошел провер
userNotFoundMessage=Пользователь не найден. userNotFoundMessage=Пользователь не найден.
incorrectPasswordMessage=Текущий пароль неверен. incorrectPasswordMessage=Текущий пароль неверен.
usernameExistsMessage=Новое имя пользователя уже существует. usernameExistsMessage=Новое имя пользователя уже существует.
invalidUsernameMessage=Недопустимое имя пользователя, Имя пользователя должно содержать только буквы алфавита и цифры. invalidUsernameMessage=Неверное имя пользователя. Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты.
deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему. deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему.
deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено. deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено.
info=Info
downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя
downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться. downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Ошибка error=Ошибка
oops=Ой! oops=Ой!
help=Помощь help=Помощь
@ -70,7 +71,7 @@ visitGithub=Посетить репозиторий на GitHub
donate=Пожертвовать donate=Пожертвовать
color=Цвет color=Цвет
sponsor=Спонсор sponsor=Спонсор
info=Info
@ -172,7 +173,7 @@ adminUserSettings.header=Настройки контроля пользоват
adminUserSettings.admin=Администратор adminUserSettings.admin=Администратор
adminUserSettings.user=Пользователь adminUserSettings.user=Пользователь
adminUserSettings.addUser=Добавить нового пользователя adminUserSettings.addUser=Добавить нового пользователя
adminUserSettings.usernameInfo=Имя пользователя должно содержать только буквы и цифры, без пробелов и специальных символов. adminUserSettings.usernameInfo=Имя пользователя может содержать только буквы, цифры и следующие специальные символы @._+- или должно быть действительным адресом электронной почты.
adminUserSettings.roles=Роли adminUserSettings.roles=Роли
adminUserSettings.role=Роль adminUserSettings.role=Роль
adminUserSettings.actions=Действия adminUserSettings.actions=Действия
@ -184,6 +185,7 @@ adminUserSettings.internalApiUser=Внутренний пользователь
adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему
adminUserSettings.submit=Сохранить пользователя adminUserSettings.submit=Сохранить пользователя
adminUserSettings.changeUserRole=Изменить роль пользователя adminUserSettings.changeUserRole=Изменить роль пользователя
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -803,7 +805,7 @@ pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1
#multiTool #multiTool
multiTool.title=Мультиинструмент PDF multiTool.title=Мультиинструмент PDF
multiTool.header=Мультиинструмент PDF multiTool.header=Мультиинструмент PDF
multiTool.uploadPrompts=Пожалуйста, загрузите PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Просмотреть PDF viewPdf.title=Просмотреть PDF
@ -1058,7 +1060,7 @@ licenses.version=Версия
licenses.license=Лицензия licenses.license=Лицензия
# error #error
error.sorry=Извините за проблему! error.sorry=Извините за проблему!
error.needHelp=Нужна помощь / Нашли проблему? error.needHelp=Нужна помощь / Нашли проблему?
error.contactTip=Если у вас все еще есть проблемы, не стесняйтесь обращаться к нам за помощью. Вы можете отправить заявку на нашей странице GitHub или связаться с нами через Discord: error.contactTip=Если у вас все еще есть проблемы, не стесняйтесь обращаться к нам за помощью. Вы можете отправить заявку на нашей странице GitHub или связаться с нами через Discord:

View File

@ -59,6 +59,8 @@ deleteCurrentUserMessage=Nie je možné zmazať aktuálne prihláseného použí
deleteUsernameExistsMessage=Používateľské meno neexistuje a nemôže byť zmazané. deleteUsernameExistsMessage=Používateľské meno neexistuje a nemôže byť zmazané.
downgradeCurrentUserMessage=Nie je možné znížiť rolu aktuálneho používateľa downgradeCurrentUserMessage=Nie je možné znížiť rolu aktuálneho používateľa
downgradeCurrentUserLongMessage=Nie je možné znížiť rolu aktuálneho používateľa. Preto, aktuálny používateľ nebude zobrazený. downgradeCurrentUserLongMessage=Nie je možné znížiť rolu aktuálneho používateľa. Preto, aktuálny používateľ nebude zobrazený.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Chyba error=Chyba
oops=Ups! oops=Ups!
help=Pomoc help=Pomoc
@ -69,6 +71,7 @@ visitGithub=Navštíviť GitHub repozitár
donate=Darovať donate=Darovať
color=Farba color=Farba
sponsor=Sponzorovať sponsor=Sponzorovať
info=Info
@ -102,12 +105,18 @@ pipelineOptions.validateButton=Overiť
############# #############
# NAVBAR # # NAVBAR #
############# #############
navbar.convert=Konvertovať navbar.favorite=Favorites
navbar.security=Bezpečnosť
navbar.other=Rôzne
navbar.darkmode=Tmavý režim navbar.darkmode=Tmavý režim
navbar.pageOps=Operácie so stránkami navbar.language=Languages
navbar.settings=Nastavenia navbar.settings=Nastavenia
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Sign & Security
navbar.sections.advance=Advanced
navbar.sections.edit=View & Edit
############# #############
# SETTINGS # # SETTINGS #
@ -176,6 +185,7 @@ adminUserSettings.internalApiUser=Interný API používateľ
adminUserSettings.forceChange=Donútiť používateľa zmeniť heslo pri prihlásení adminUserSettings.forceChange=Donútiť používateľa zmeniť heslo pri prihlásení
adminUserSettings.submit=Uložiť používateľa adminUserSettings.submit=Uložiť používateľa
adminUserSettings.changeUserRole=Zmeniť rolu používateľa adminUserSettings.changeUserRole=Zmeniť rolu používateľa
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -742,6 +752,7 @@ extractImages.submit=Extrahovať
fileToPDF.title=Súbor do PDF fileToPDF.title=Súbor do PDF
fileToPDF.header=Konvertovať akýkoľvek súbor do PDF fileToPDF.header=Konvertovať akýkoľvek súbor do PDF
fileToPDF.credit=Táto služba používa LibreOffice a Unoconv pre konverziu súborov. fileToPDF.credit=Táto služba používa LibreOffice a Unoconv pre konverziu súborov.
fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Podporované typy súborov by mali zahŕňať nižšie uvedené, avšak pre úplný aktualizovaný zoznam podporovaných formátov, prosím, odkazujte na dokumentáciu LibreOffice fileToPDF.supportedFileTypes=Podporované typy súborov by mali zahŕňať nižšie uvedené, avšak pre úplný aktualizovaný zoznam podporovaných formátov, prosím, odkazujte na dokumentáciu LibreOffice
fileToPDF.submit=Konvertovať do PDF fileToPDF.submit=Konvertovať do PDF
@ -794,7 +805,7 @@ pdfOrganiser.placeholder=(napr. 1,3,2 alebo 4-8,2,10-12 alebo 2n-1)
#multiTool #multiTool
multiTool.title=PDF Multi Nástroj multiTool.title=PDF Multi Nástroj
multiTool.header=PDF Multi Nástroj multiTool.header=PDF Multi Nástroj
multiTool.uploadPrompts=Prosím, nahrajte PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Zobraziť PDF viewPdf.title=Zobraziť PDF
@ -1049,7 +1060,7 @@ licenses.version=Verzia
licenses.license=Licencia licenses.license=Licencia
# error #error
error.sorry=Ospravedlňujeme sa za problém! error.sorry=Ospravedlňujeme sa za problém!
error.needHelp=Potrebujete pomoc / Našli ste problém? error.needHelp=Potrebujete pomoc / Našli ste problém?
error.contactTip=Ak máte stále problémy, neváhajte nás kontaktovať pre pomoc. Môžete podať tiket na našej stránke GitHub alebo nás kontaktovať cez Discord: error.contactTip=Ak máte stále problémy, neváhajte nás kontaktovať pre pomoc. Môžete podať tiket na našej stránke GitHub alebo nás kontaktovať cez Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Korisnik nije autentifikovan.
userNotFoundMessage=Korisnik nije pronađen. userNotFoundMessage=Korisnik nije pronađen.
incorrectPasswordMessage=Trenutna šifra je netačna. incorrectPasswordMessage=Trenutna šifra je netačna.
usernameExistsMessage=Novi korisnik već postoji usernameExistsMessage=Novi korisnik već postoji
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika
downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan. downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Podešavanja kontrole korisnika za administratora
adminUserSettings.admin=Administrator adminUserSettings.admin=Administrator
adminUserSettings.user=Korisnik adminUserSettings.user=Korisnik
adminUserSettings.addUser=Dodaj novog korisnika adminUserSettings.addUser=Dodaj novog korisnika
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Uloge adminUserSettings.roles=Uloge
adminUserSettings.role=Uloga adminUserSettings.role=Uloga
adminUserSettings.actions=Akcije adminUserSettings.actions=Akcije
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi
adminUserSettings.submit=Sačuvaj korisnika adminUserSettings.submit=Sačuvaj korisnika
adminUserSettings.changeUserRole=Promenite ulogu korisnika adminUserSettings.changeUserRole=Promenite ulogu korisnika
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=PDF Multi Alatka multiTool.title=PDF Multi Alatka
multiTool.header=PDF Multi Alatka multiTool.header=PDF Multi Alatka
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Prikaz viewPdf.title=Prikaz
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=User not authenticated.
userNotFoundMessage=User not found. userNotFoundMessage=User not found.
incorrectPasswordMessage=Current password is incorrect. incorrectPasswordMessage=Current password is incorrect.
usernameExistsMessage=New Username already exists. usernameExistsMessage=New Username already exists.
invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. invalidUsernameMessage=Invalid username, username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
deleteCurrentUserMessage=Cannot delete currently logged in user. deleteCurrentUserMessage=Cannot delete currently logged in user.
deleteUsernameExistsMessage=The username does not exist and cannot be deleted. deleteUsernameExistsMessage=The username does not exist and cannot be deleted.
downgradeCurrentUserMessage=Kan inte nedgradera nuvarande användares roll downgradeCurrentUserMessage=Kan inte nedgradera nuvarande användares roll
downgradeCurrentUserLongMessage=Kan inte nedgradera nuvarande användares roll. Därför kommer den aktuella användaren inte att visas. downgradeCurrentUserLongMessage=Kan inte nedgradera nuvarande användares roll. Därför kommer den aktuella användaren inte att visas.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Admin User Control Settings
adminUserSettings.admin=Admin adminUserSettings.admin=Admin
adminUserSettings.user=User adminUserSettings.user=User
adminUserSettings.addUser=Add New User adminUserSettings.addUser=Add New User
adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.usernameInfo=Username can only contain letters, numbers and the following special characters @._+- or must be a valid email address.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Role adminUserSettings.role=Role
adminUserSettings.actions=Actions adminUserSettings.actions=Actions
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Internal API User
adminUserSettings.forceChange=Force user to change password on login adminUserSettings.forceChange=Force user to change password on login
adminUserSettings.submit=Save User adminUserSettings.submit=Save User
adminUserSettings.changeUserRole=Ändra användarens roll adminUserSettings.changeUserRole=Ändra användarens roll
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
#multiTool #multiTool
multiTool.title=PDF-multiverktyg multiTool.title=PDF-multiverktyg
multiTool.header=PDF Multi-verktyg multiTool.header=PDF Multi-verktyg
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=View PDF viewPdf.title=View PDF
@ -1057,7 +1060,7 @@ licenses.version=Version
licenses.license=License licenses.license=License
# error #error
error.sorry=Sorry for the issue! error.sorry=Sorry for the issue!
error.needHelp=Need help / Found an issue? error.needHelp=Need help / Found an issue?
error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Kullanıcı doğrulanmadı.
userNotFoundMessage=Kullanıcı bulunamadı. userNotFoundMessage=Kullanıcı bulunamadı.
incorrectPasswordMessage=Mevcut şifre yanlış. incorrectPasswordMessage=Mevcut şifre yanlış.
usernameExistsMessage=Yeni Kullanıcı Adı zaten var. usernameExistsMessage=Yeni Kullanıcı Adı zaten var.
invalidUsernameMessage=Geçersiz kullanıcı adı, Kullanıcı adı yalnızca alfabe karakterleri ve sayılar içermelidir. invalidUsernameMessage=Geçersiz kullanıcı adı, kullanıcı adı yalnızca harf, rakam ve aşağıdaki özel karakterleri @._+- içerebilir veya geçerli bir e-posta adresi olmalıdır.
deleteCurrentUserMessage=Şu anda oturum açmış olan kullanıcı silinemiyor. deleteCurrentUserMessage=Şu anda oturum açmış olan kullanıcı silinemiyor.
deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez. deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez.
downgradeCurrentUserMessage=Mevcut kullanıcının rolü düşürülemiyor downgradeCurrentUserMessage=Mevcut kullanıcının rolü düşürülemiyor
downgradeCurrentUserLongMessage=Mevcut kullanıcının rolü düşürülemiyor. Bu nedenle, mevcut kullanıcı gösterilmeyecektir. downgradeCurrentUserLongMessage=Mevcut kullanıcının rolü düşürülemiyor. Bu nedenle, mevcut kullanıcı gösterilmeyecektir.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Hata error=Hata
oops=Tüh! oops=Tüh!
help=Yardım help=Yardım
@ -68,8 +70,8 @@ seeDockerHub=Docker Hub'a bakın
visitGithub=Github Deposunu Ziyaret Edin visitGithub=Github Deposunu Ziyaret Edin
donate=Bağış Yapın donate=Bağış Yapın
color=Renk color=Renk
info=Info
sponsor=Bağış sponsor=Bağış
info=Info
@ -171,7 +173,7 @@ adminUserSettings.header=Yönetici Kullanıcı Kontrol Ayarları
adminUserSettings.admin=Yönetici adminUserSettings.admin=Yönetici
adminUserSettings.user=Kullanıcı adminUserSettings.user=Kullanıcı
adminUserSettings.addUser=Yeni Kullanıcı Ekle adminUserSettings.addUser=Yeni Kullanıcı Ekle
adminUserSettings.usernameInfo=Kullanıcı adı yalnızca harf ve rakamlardan oluşmalı, boşluk veya özel karakter içermemelidir. adminUserSettings.usernameInfo=Kullanıcı adı yalnızca harf, rakam ve aşağıdaki özel karakterleri @._+- içerebilir veya geçerli bir e-posta adresi olmalıdır.
adminUserSettings.roles=Roller adminUserSettings.roles=Roller
adminUserSettings.role=Rol adminUserSettings.role=Rol
adminUserSettings.actions=Eylemler adminUserSettings.actions=Eylemler
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Dahili API Kullanıcısı
adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla
adminUserSettings.submit=Kullanıcıyı Kaydet adminUserSettings.submit=Kullanıcıyı Kaydet
adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(örn. 1,3,2 veya 4-8,2,10-12 veya 2n-1)
#multiTool #multiTool
multiTool.title=PDF Çoklu Araç multiTool.title=PDF Çoklu Araç
multiTool.header=PDF Çoklu Araç multiTool.header=PDF Çoklu Araç
multiTool.uploadPrompts=Lütfen PDF Yükleyin multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=PDF Görüntüle viewPdf.title=PDF Görüntüle
@ -1057,7 +1060,7 @@ licenses.version=Versiyon
licenses.license=Lisans licenses.license=Lisans
# error #error
error.sorry=Sorun için özür dileriz! error.sorry=Sorun için özür dileriz!
error.needHelp=Yardıma mı ihtiyacınız var / Bir sorun mu buldunuz? error.needHelp=Yardıma mı ihtiyacınız var / Bir sorun mu buldunuz?
error.contactTip=Hala sorun yaşıyorsanız, yardım için bize ulaşmaktan çekinmeyin. GitHub sayfamızdan bir bilet gönderebilir veya Discord üzerinden bizimle iletişime geçebilirsiniz: error.contactTip=Hala sorun yaşıyorsanız, yardım için bize ulaşmaktan çekinmeyin. GitHub sayfamızdan bir bilet gönderebilir veya Discord üzerinden bizimle iletişime geçebilirsiniz:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=Користувач не пройшов перевір
userNotFoundMessage=Користувача не знайдено. userNotFoundMessage=Користувача не знайдено.
incorrectPasswordMessage=Поточний пароль невірний. incorrectPasswordMessage=Поточний пароль невірний.
usernameExistsMessage=Нове ім'я користувача вже існує. usernameExistsMessage=Нове ім'я користувача вже існує.
invalidUsernameMessage=Недійсне ім'я користувача, Ім'я користувача повинно містити тільки літери алфавіту та цифри. invalidUsernameMessage=Недійсне ім’я користувача, ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему. deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему.
deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено. deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено.
downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача
downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься. downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься.
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Oops! oops=Oops!
help=Help help=Help
@ -171,7 +173,7 @@ adminUserSettings.header=Налаштування контролю корист
adminUserSettings.admin=Адміністратор adminUserSettings.admin=Адміністратор
adminUserSettings.user=Користувач adminUserSettings.user=Користувач
adminUserSettings.addUser=Додати нового користувача adminUserSettings.addUser=Додати нового користувача
adminUserSettings.usernameInfo=Ім'я користувача має містити тільки літери та цифри, без пробілів та спеціальних символів. adminUserSettings.usernameInfo=Ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою.
adminUserSettings.roles=Ролі adminUserSettings.roles=Ролі
adminUserSettings.role=Роль adminUserSettings.role=Роль
adminUserSettings.actions=Дії adminUserSettings.actions=Дії
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=Внутрішній користувач API
adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему
adminUserSettings.submit=Зберегти користувача adminUserSettings.submit=Зберегти користувача
adminUserSettings.changeUserRole=Змінити роль користувача adminUserSettings.changeUserRole=Змінити роль користувача
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n
#multiTool #multiTool
multiTool.title=Мультіінструмент PDF multiTool.title=Мультіінструмент PDF
multiTool.header=Мультіінструмент PDF multiTool.header=Мультіінструмент PDF
multiTool.uploadPrompts=Будь ласка, завантажте PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=Переглянути PDF viewPdf.title=Переглянути PDF
@ -1057,7 +1060,7 @@ licenses.version=Версія
licenses.license=Ліцензія licenses.license=Ліцензія
# error #error
error.sorry=Вибачте за незручності! error.sorry=Вибачте за незручності!
error.needHelp=Потрібна допомога / Знайшли проблему? error.needHelp=Потрібна допомога / Знайшли проблему?
error.contactTip=Якщо у вас досі виникають проблеми, не соромтеся звертатися до нас за допомогою. Ви можете надіслати запит на нашій сторінці GitHub або зв'язатися з нами через Discord: error.contactTip=Якщо у вас досі виникають проблеми, не соромтеся звертатися до нас за допомогою. Ви можете надіслати запит на нашій сторінці GitHub або зв'язатися з нами через Discord:

View File

@ -54,11 +54,13 @@ notAuthenticatedMessage=用户未经过身份验证。
userNotFoundMessage=未找到用户。 userNotFoundMessage=未找到用户。
incorrectPasswordMessage=当前密码不正确。 incorrectPasswordMessage=当前密码不正确。
usernameExistsMessage=新用户名已存在。 usernameExistsMessage=新用户名已存在。
invalidUsernameMessage=用户名无效,用户名只能由字母字符和数字组成 invalidUsernameMessage=用户名无效,用户名只能包含字母、数字和以下特殊字符@._+- 或必须是有效的电子邮件地址
deleteCurrentUserMessage=无法删除当前登录的用户。 deleteCurrentUserMessage=无法删除当前登录的用户。
deleteUsernameExistsMessage=用户名不存在,无法删除。 deleteUsernameExistsMessage=用户名不存在,无法删除。
downgradeCurrentUserMessage=无法降级当前用户的角色 downgradeCurrentUserMessage=无法降级当前用户的角色
downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。 downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=错误 error=错误
oops=哎呀! oops=哎呀!
help=帮助 help=帮助
@ -171,7 +173,7 @@ adminUserSettings.header=管理员用户控制设置
adminUserSettings.admin=管理员 adminUserSettings.admin=管理员
adminUserSettings.user=用户 adminUserSettings.user=用户
adminUserSettings.addUser=添加新用户 adminUserSettings.addUser=添加新用户
adminUserSettings.usernameInfo=用户名只能由字母和数字组成,不能包含空格或特殊字符 adminUserSettings.usernameInfo=用户名只能包含字母、数字和以下特殊字符@._+-,或者必须是有效的电子邮件地址
adminUserSettings.roles=角色 adminUserSettings.roles=角色
adminUserSettings.role=角色 adminUserSettings.role=角色
adminUserSettings.actions=操作 adminUserSettings.actions=操作
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=内部API用户
adminUserSettings.forceChange=强制用户在登录时更改用户名/密码 adminUserSettings.forceChange=强制用户在登录时更改用户名/密码
adminUserSettings.submit=保存用户 adminUserSettings.submit=保存用户
adminUserSettings.changeUserRole=更改用户角色 adminUserSettings.changeUserRole=更改用户角色
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1
#multiTool #multiTool
multiTool.title=PDF多功能工具 multiTool.title=PDF多功能工具
multiTool.header=PDF多功能工具 multiTool.header=PDF多功能工具
multiTool.uploadPrompts=上传PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=浏览PDF viewPdf.title=浏览PDF
@ -1057,7 +1060,7 @@ licenses.version=版本
licenses.license=许可证 licenses.license=许可证
# error #error
error.sorry=对此问题感到抱歉! error.sorry=对此问题感到抱歉!
error.needHelp=需要帮助 / 发现问题? error.needHelp=需要帮助 / 发现问题?
error.contactTip=如果你仍然遇到问题不要犹豫向我们寻求帮助。你可以在我们的GitHub页面上提交工单或者通过Discord与我们联系 error.contactTip=如果你仍然遇到问题不要犹豫向我们寻求帮助。你可以在我们的GitHub页面上提交工单或者通过Discord与我们联系

View File

@ -54,12 +54,13 @@ notAuthenticatedMessage=使用者未認證。
userNotFoundMessage=找不到使用者。 userNotFoundMessage=找不到使用者。
incorrectPasswordMessage=目前密碼不正確。 incorrectPasswordMessage=目前密碼不正確。
usernameExistsMessage=新使用者名稱已存在。 usernameExistsMessage=新使用者名稱已存在。
invalidUsernameMessage=使用者名無效,使用者名只能包含字母字元和數位 invalidUsernameMessage=使用者名稱無效,使用者名稱只能包含字母、數字和以下特殊字元@._+- 或必須是有效的電子郵件地址
deleteCurrentUserMessage=無法刪除目前登錄的使用者。 deleteCurrentUserMessage=無法刪除目前登錄的使用者。
deleteUsernameExistsMessage=使用者名不存在,無法刪除。 deleteUsernameExistsMessage=使用者名不存在,無法刪除。
info=Info
downgradeCurrentUserMessage=無法降級目前使用者的角色 downgradeCurrentUserMessage=無法降級目前使用者的角色
downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。 downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。
userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=The user already exists as an web user.
error=錯誤 error=錯誤
oops=哎呀! oops=哎呀!
help=幫助 help=幫助
@ -70,6 +71,7 @@ visitGithub=訪問Github存儲庫
donate=捐贈 donate=捐贈
color=顏色 color=顏色
sponsor=贊助 sponsor=贊助
info=Info
@ -171,7 +173,7 @@ adminUserSettings.header=管理使用者控制設定
adminUserSettings.admin=管理員 adminUserSettings.admin=管理員
adminUserSettings.user=使用者 adminUserSettings.user=使用者
adminUserSettings.addUser=新增使用者 adminUserSettings.addUser=新增使用者
adminUserSettings.usernameInfo=使用者名只能包含字母和數位,不能包含空格或特殊字元 adminUserSettings.usernameInfo=使用者名稱只能包含字母、數字和以下特殊字元@._+-,或必須是有效的電子郵件地址
adminUserSettings.roles=角色 adminUserSettings.roles=角色
adminUserSettings.role=角色 adminUserSettings.role=角色
adminUserSettings.actions=操作 adminUserSettings.actions=操作
@ -183,6 +185,7 @@ adminUserSettings.internalApiUser=內部 API 使用者
adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼 adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼
adminUserSettings.submit=儲存 adminUserSettings.submit=儲存
adminUserSettings.changeUserRole=更改使用者身份 adminUserSettings.changeUserRole=更改使用者身份
adminUserSettings.authenticated=Authenticated
############# #############
# HOME-PAGE # # HOME-PAGE #
@ -802,7 +805,7 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1
#multiTool #multiTool
multiTool.title=PDF 多工具 multiTool.title=PDF 多工具
multiTool.header=PDF 多工具 multiTool.header=PDF 多工具
multiTool.uploadPrompts=Please Upload PDF multiTool.uploadPrompts=File Name
#view pdf #view pdf
viewPdf.title=檢視 PDF viewPdf.title=檢視 PDF
@ -1057,7 +1060,7 @@ licenses.version=版本
licenses.license=許可證 licenses.license=許可證
# error #error
error.sorry=對於這個問題,我們感到抱歉! error.sorry=對於這個問題,我們感到抱歉!
error.needHelp=需要幫助/發現了一個問題? error.needHelp=需要幫助/發現了一個問題?
error.contactTip=如果你仍然遇到問題請不要猶豫隨時向我們尋求幫助。你可以在我們的GitHub頁面提交工單或通過Discord與我們聯繋 error.contactTip=如果你仍然遇到問題請不要猶豫隨時向我們尋求幫助。你可以在我們的GitHub頁面提交工單或通過Discord與我們聯繋

View File

@ -7,12 +7,15 @@ security:
csrfDisabled: true csrfDisabled: true
loginAttemptCount: 5 # lock user account after 5 tries loginAttemptCount: 5 # lock user account after 5 tries
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
#oauth2: # oauth2:
# enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) # enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
# issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point # issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point
# clientId: "" # Client ID from your provider # clientId: "" # Client ID from your provider
# clientSecret: "" # Client Secret from your provider # clientSecret: "" # Client Secret from your provider
# autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users # autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
# useAsUsername: "email" # Default is 'email'; custom fields can be used as the username
# scopes: "openid, profile, email" # Specify the scopes for which the application will request permissions
# provider: "google" # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
system: system:
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)

File diff suppressed because one or more lines are too long

View File

@ -1,300 +1,277 @@
<!DOCTYPE html> <!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" <html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="http://www.thymeleaf.org">
xmlns:th="http://www.thymeleaf.org"> <head>
<head>
<th:block th:insert="~{fragments/common :: head(title=#{account.title})}"></th:block> <th:block th:insert="~{fragments/common :: head(title=#{account.title})}"></th:block>
</head> </head>
<body> <body>
<th:block th:insert="~{fragments/common :: game}"></th:block> <th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container"> <div id="page-container">
<div id="content-wrap"> <div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block> <th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br> <br><br>
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-9" id="bg-card"> <div class="col-md-9">
<!-- User Settings Title --> <!-- User Settings Title -->
<h2 class="text-center" th:text="#{account.accountSettings}">User Settings</h2> <h2 class="text-center" th:text="#{account.accountSettings}">User Settings</h2>
<th:block th:if="${param.messageType != null and param.messageType.size() > 0}"> <th:block th:if="${messageType}">
<div th:if="${param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger"> <div class="alert alert-danger">
<span th:text="#{notAuthenticatedMessage}">Default message if not found</span> <span th:text="#{${messageType}}">Default message if not found</span>
</div> </div>
<div th:if="${param.messageType[0] == 'userNotFound'}" class="alert alert-danger"> </th:block>
<span th:text="#{userNotFoundMessage}">Default message if not found</span> <!-- At the top of the user settings -->
</div> <h3 class="text-center"><span th:text="#{welcome} + ' ' + ${username}">User</span>!</h3>
<div th:if="${param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger"> <th:block th:if="${error}">
<span th:text="#{incorrectPasswordMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType[0] == 'invalidUsername'}" class="alert alert-danger">
<span th:text="#{invalidUsernameMessage}">Default message if not found</span>
</div>
</th:block>
<!-- At the top of the user settings -->
<h3 class="text-center"><span th:text="#{welcome} + ' ' + ${username}">User</span>!</h3>
<th:block th:if="${error}">
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
<span th:text="${error}">Error Message</span> <span th:text="${error}">Error Message</span>
</div> </div>
</th:block> </th:block>
<!-- Change Username Form --> <!-- Change Username Form -->
<h4 th:text="#{account.changeUsername}">Change Username?</h4> <th:block th:if="${!oAuth2Login}">
<form th:if="${!oAuth2Login}" id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-username" method="post"> <h4 th:text="#{account.changeUsername}">Change Username?</h4>
<div class="mb-3"> <form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-username" method="post">
<label for="newUsername" th:text="#{account.newUsername}">Change Username</label> <div class="mb-3">
<input type="text" class="form-control" name="newUsername" id="newUsername" <label for="newUsername" th:text="#{account.newUsername}">Change Username</label>
th:placeholder="#{account.newUsername}"> <input type="text" class="form-control" name="newUsername" id="newUsername" th:placeholder="#{account.newUsername}">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="currentPassword" th:text="#{password}">Password</label> <label for="currentPassword" th:text="#{password}">Password</label>
<input type="password" class="form-control" name="currentPassword" id="currentPassword" <input type="password" class="form-control" name="currentPassword" id="currentPassword" th:placeholder="#{password}">
th:placeholder="#{password}"> </div>
</div> <div class="mb-3">
<div class="mb-3"> <button type="submit" class="btn btn-primary" th:text="#{account.changeUsername}">Change Username</button>
<button type="submit" class="btn btn-primary" th:text="#{account.changeUsername}">Change </div>
Username</button> </form>
</div> </th:block>
</form>
<!-- Change Password Form -->
<h4 th:if="${!oAuth2Login}" th:text="#{account.changePassword}">Change Password?</h4>
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-password" method="post">
<div class="mb-3">
<label for="currentPassword" th:text="#{account.oldPassword}">Old Password</label>
<input type="password" class="form-control" name="currentPassword" id="currentPasswordPassword"
th:placeholder="#{account.oldPassword}">
</div>
<div class="mb-3">
<label for="newPassword" th:text="#{account.newPassword}">New Password</label>
<input type="password" class="form-control" name="newPassword" id="newPassword"
th:placeholder="#{account.newPassword}">
</div>
<div class="mb-3">
<label for="confirmNewPassword" th:text="#{account.confirmNewPassword}">Confirm New Password</label>
<input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword"
th:placeholder="#{account.confirmNewPassword}">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary" th:text="#{account.changePassword}">Change
Password</button>
</div>
</form>
<!-- Change Password Form -->
<th:block th:if="${!oAuth2Login}">
<h4 th:text="#{account.changePassword}">Change Password?</h4>
<form id="bg-card" class="mt-4 mb-4" action="api/v1/user/change-password" method="post">
<div class="mb-3">
<label for="currentPassword" th:text="#{account.oldPassword}">Old Password</label>
<input type="password" class="form-control" name="currentPassword" id="currentPasswordPassword" th:placeholder="#{account.oldPassword}">
</div>
<div class="mb-3">
<label for="newPassword" th:text="#{account.newPassword}">New Password</label>
<input type="password" class="form-control" name="newPassword" id="newPassword" th:placeholder="#{account.newPassword}">
</div>
<div class="mb-3">
<label for="confirmNewPassword" th:text="#{account.confirmNewPassword}">Confirm New Password</label>
<input type="password" class="form-control" name="confirmNewPassword" id="confirmNewPassword" th:placeholder="#{account.confirmNewPassword}">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary" th:text="#{account.changePassword}">Change Password</button>
</div>
</form>
</th:block>
<!-- API Key Form --> <!-- API Key Form -->
<h4 th:text="#{account.yourApiKey}">API Key</h4> <h4 th:text="#{account.yourApiKey}">API Key</h4>
<div class="card mt-4 mb-4"> <div class="card mt-4 mb-4">
<div class="card-header" th:text="#{account.yourApiKey}"></div> <div class="card-header" th:text="#{account.yourApiKey}"></div>
<div class="card-body"> <div class="card-body">
<div class="input-group mb-3"> <div class="input-group mb-3">
<input type="password" class="form-control" id="apiKey" th:placeholder="#{account.yourApiKey}" <input type="password" class="form-control" id="apiKey" th:placeholder="#{account.yourApiKey}" readonly>
readonly> <div class="input-group-append">
<div class="input-group-append"> <button class="btn btn-secondary" id="copyBtn" type="button" onclick="copyToClipboard()">
<button class="btn btn-secondary" id="copyBtn" type="button" onclick="copyToClipboard()"> <span class="material-symbols-rounded">
<span class="material-symbols-rounded">
content_copy content_copy
</span> </span>
</button> </button>
<button class="btn btn-secondary" id="showBtn" type="button" onclick="showApiKey()"> <button class="btn btn-secondary" id="showBtn" type="button" onclick="showApiKey()">
<span class="material-symbols-rounded" id="eyeIcon"> <span class="material-symbols-rounded" id="eyeIcon">
visibility visibility
</span> </span>
</button> </button>
<button class="btn btn-secondary" id="refreshBtn" type="button" onclick="refreshApiKey()"> <button class="btn btn-secondary" id="refreshBtn" type="button" onclick="refreshApiKey()">
<span class="material-symbols-rounded"> <span class="material-symbols-rounded">
refresh refresh
</span> </span>
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<script> <script>
function copyToClipboard() { function copyToClipboard() {
const apiKeyElement = document.getElementById("apiKey"); const apiKeyElement = document.getElementById("apiKey");
apiKeyElement.select(); apiKeyElement.select();
document.execCommand("copy"); document.execCommand("copy");
}
function showApiKey() {
const apiKeyElement = document.getElementById("apiKey");
const copyBtn = document.getElementById("copyBtn");
const eyeIcon = document.getElementById("eyeIcon");
if (apiKeyElement.type === "password") {
apiKeyElement.type = "text";
eyeIcon.textContent = "visibility_off";
copyBtn.disabled = false; // Enable copy button when API key is visible
} else {
apiKeyElement.type = "password";
eyeIcon.textContent = "visibility";
copyBtn.disabled = true; // Disable copy button when API key is hidden
} }
}
document.addEventListener("DOMContentLoaded", async function () { function showApiKey() {
try { const apiKeyElement = document.getElementById("apiKey");
let response = await fetch('/api/v1/user/get-api-key', { method: 'POST' }); const copyBtn = document.getElementById("copyBtn");
if (response.status === 200) { const eyeIcon = document.getElementById("eyeIcon");
let apiKey = await response.text(); if (apiKeyElement.type === "password") {
manageUIState(apiKey); apiKeyElement.type = "text";
eyeIcon.textContent = "visibility_off";
copyBtn.disabled = false; // Enable copy button when API key is visible
} else { } else {
manageUIState(null); apiKeyElement.type = "password";
eyeIcon.textContent = "visibility";
copyBtn.disabled = true; // Disable copy button when API key is hidden
} }
} catch (error) {
console.error('There was an error:', error);
} }
});
async function refreshApiKey() { document.addEventListener("DOMContentLoaded", async function() {
try { try {
let response = await fetch('/api/v1/user/update-api-key', { method: 'POST' }); let response = await fetch('/api/v1/user/get-api-key', { method: 'POST' });
if (response.status === 200) { if (response.status === 200) {
let apiKey = await response.text(); let apiKey = await response.text();
manageUIState(apiKey); manageUIState(apiKey);
document.getElementById("apiKey").type = 'text'; } else {
document.getElementById("copyBtn").disabled = false; manageUIState(null);
} else { }
alert('Error refreshing API key.'); } catch (error) {
} console.error('There was an error:', error);
} catch (error) {
console.error('There was an error:', error);
}
}
function manageUIState(apiKey) {
const apiKeyElement = document.getElementById("apiKey");
const showBtn = document.getElementById("showBtn");
const copyBtn = document.getElementById("copyBtn");
if (apiKey && apiKey.trim().length > 0) {
apiKeyElement.value = apiKey;
showBtn.disabled = false;
copyBtn.disabled = true;
} else {
apiKeyElement.value = "";
showBtn.disabled = true;
copyBtn.disabled = true;
}
}
document.addEventListener("DOMContentLoaded", function () {
const form = document.querySelector('form[action="api/v1/user/change-password"]');
form.addEventListener('submit', function (event) {
const newPassword = document.getElementById('newPassword').value;
const confirmNewPassword = document.getElementById('confirmNewPassword').value;
if (newPassword !== confirmNewPassword) {
alert('New Password and Confirm New Password must match.');
event.preventDefault(); // Prevent form submission
} }
}); });
});
</script>
<h4 th:text="#{account.syncTitle}">Sync browser settings with Account</h4> async function refreshApiKey() {
<div id="bg-card" class="container mt-4"> try {
<h3 th:text="#{account.settingsCompare}">Settings Comparison:</h3> let response = await fetch('/api/v1/user/update-api-key', { method: 'POST' });
<table id="settingsTable" class="table table-bordered table-sm table-striped"> if (response.status === 200) {
<thead> let apiKey = await response.text();
<tr> manageUIState(apiKey);
<th th:text="#{account.property}">Property</th> document.getElementById("apiKey").type = 'text';
<th th:text="#{account.accountSettings}">Account Setting</th> document.getElementById("copyBtn").disabled = false;
<th th:text="#{account.webBrowserSettings}">Web Browser Setting</th> } else {
</tr> alert('Error refreshing API key.');
</thead> }
<tbody> } catch (error) {
<!-- This will be dynamically populated by JavaScript --> console.error('There was an error:', error);
</tbody> }
</table> }
<div class="buttons-container mt-3 text-center"> function manageUIState(apiKey) {
<button id="syncToBrowser" class="btn btn-primary btn-sm" th:text="#{account.syncToBrowser}">Sync const apiKeyElement = document.getElementById("apiKey");
Account -> Browser</button> const showBtn = document.getElementById("showBtn");
<button id="syncToAccount" class="btn btn-secondary btn-sm" th:text="#{account.syncToAccount}">Sync const copyBtn = document.getElementById("copyBtn");
Account <- Browser</button>
if (apiKey && apiKey.trim().length > 0) {
apiKeyElement.value = apiKey;
showBtn.disabled = false;
copyBtn.disabled = true;
} else {
apiKeyElement.value = "";
showBtn.disabled = true;
copyBtn.disabled = true;
}
}
document.addEventListener("DOMContentLoaded", function() {
const form = document.querySelector('form[action="api/v1/user/change-password"]');
form.addEventListener('submit', function(event) {
const newPassword = document.getElementById('newPassword').value;
const confirmNewPassword = document.getElementById('confirmNewPassword').value;
if (newPassword !== confirmNewPassword) {
alert('New Password and Confirm New Password must match.');
event.preventDefault(); // Prevent form submission
}
});
});
</script>
<h4 th:text="#{account.syncTitle}">Sync browser settings with Account</h4>
<div id="bg-card" class="container mt-4">
<h3 th:text="#{account.settingsCompare}">Settings Comparison:</h3>
<table id="settingsTable" class="table table-bordered table-sm table-striped">
<thead>
<tr>
<th th:text="#{account.property}">Property</th>
<th th:text="#{account.accountSettings}">Account Setting</th>
<th th:text="#{account.webBrowserSettings}">Web Browser Setting</th>
</tr>
</thead>
<tbody>
<!-- This will be dynamically populated by JavaScript -->
</tbody>
</table>
<div class="buttons-container mt-3 text-center">
<button id="syncToBrowser" class="btn btn-primary btn-sm" th:text="#{account.syncToBrowser}">Sync Account -> Browser</button>
<button id="syncToAccount" class="btn btn-secondary btn-sm" th:text="#{account.syncToAccount}">Sync Account <- Browser</button>
</div>
</div> </div>
</div>
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function() {
const settingsTableBody = document.querySelector("#settingsTable tbody"); const settingsTableBody = document.querySelector("#settingsTable tbody");
/*<![CDATA[*/ /*<![CDATA[*/
var accountSettingsString = /*[[${settings}]]*/ {}; var accountSettingsString = /*[[${settings}]]*/ {};
/*]]>*/ /*]]>*/
var accountSettings = JSON.parse(accountSettingsString); var accountSettings = JSON.parse(accountSettingsString);
let allKeys = new Set([...Object.keys(accountSettings), ...Object.keys(localStorage)]); let allKeys = new Set([...Object.keys(accountSettings), ...Object.keys(localStorage)]);
allKeys.forEach(key => { allKeys.forEach(key => {
if (key === 'debug' || key === '0' || key === '1') return; // Ignoring specific keys if(key === 'debug' || key === '0' || key === '1') return; // Ignoring specific keys
const accountValue = accountSettings[key] || '-'; const accountValue = accountSettings[key] || '-';
const browserValue = localStorage.getItem(key) || '-'; const browserValue = localStorage.getItem(key) || '-';
const row = settingsTableBody.insertRow(); const row = settingsTableBody.insertRow();
const propertyCell = row.insertCell(0); const propertyCell = row.insertCell(0);
const accountCell = row.insertCell(1); const accountCell = row.insertCell(1);
const browserCell = row.insertCell(2); const browserCell = row.insertCell(2);
propertyCell.textContent = key; propertyCell.textContent = key;
accountCell.textContent = accountValue; accountCell.textContent = accountValue;
browserCell.textContent = browserValue; browserCell.textContent = browserValue;
}); });
document.getElementById('syncToBrowser').addEventListener('click', function () { document.getElementById('syncToBrowser').addEventListener('click', function() {
// First, clear the local storage // First, clear the local storage
localStorage.clear(); localStorage.clear();
// Then, set the account settings to local storage // Then, set the account settings to local storage
for (let key in accountSettings) { for (let key in accountSettings) {
if (key !== 'debug' && key !== '0' && key !== '1') { // Only sync non-ignored keys if(key !== 'debug' && key !== '0' && key !== '1') { // Only sync non-ignored keys
localStorage.setItem(key, accountSettings[key]); localStorage.setItem(key, accountSettings[key]);
}
} }
} location.reload(); // Refresh the page after sync
location.reload(); // Refresh the page after sync });
});
document.getElementById('syncToAccount').addEventListener('click', function () { document.getElementById('syncToAccount').addEventListener('click', function() {
let form = document.createElement("form"); let form = document.createElement("form");
form.method = "POST"; form.method = "POST";
form.action = "api/v1/user/updateUserSettings"; // Your endpoint URL form.action = "api/v1/user/updateUserSettings"; // Your endpoint URL
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i); const key = localStorage.key(i);
if (key !== 'debug' && key !== '0' && key !== '1') { // Only send non-ignored keys if(key !== 'debug' && key !== '0' && key !== '1') { // Only send non-ignored keys
let hiddenField = document.createElement("input"); let hiddenField = document.createElement("input");
hiddenField.type = "hidden"; hiddenField.type = "hidden";
hiddenField.name = key; hiddenField.name = key;
hiddenField.value = localStorage.getItem(key); hiddenField.value = localStorage.getItem(key);
form.appendChild(hiddenField); form.appendChild(hiddenField);
}
} }
}
document.body.appendChild(form); document.body.appendChild(form);
form.submit(); form.submit();
});
}); });
</script>
}); <div class="mb-3 mt-4 text-center">
</script> <a href="logout" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a>
<div class="mb-3 mt-4 text-center"> <a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" href="addUsers" role="button" th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
<a href="logout" role="button" class="btn btn-danger" th:text="#{account.signOut}">Sign Out</a> </div>
<a th:if="${role == 'ROLE_ADMIN'}" class="btn btn-info" href="addUsers" role="button"
th:text="#{account.adminSettings}" target="_blank">Admin Settings</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div> </div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block> </body>
</div>
</body>
</html> </html>

View File

@ -16,9 +16,8 @@
<!-- User Settings Title --> <!-- User Settings Title -->
<h2 class="text-center" th:text="#{adminUserSettings.header}">Admin User Control Settings</h2> <h2 class="text-center" th:text="#{adminUserSettings.header}">Admin User Control Settings</h2>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'deleteCurrentUser' or param.messageType[0] == 'deleteUsernameExists')}" class="alert alert-danger"> <div th:if="${deleteMessage}" class="alert alert-danger">
<span th:if="${param.messageType[0] == 'deleteCurrentUser'}" th:text="#{deleteCurrentUserMessage}">Cannot delete currently logged in user.</span> <span th:text="#{${deleteMessage}}">Message</span>
<span th:if="${param.messageType[0] == 'deleteUsernameExists'}" th:text="#{deleteUsernameExistsMessage}">The username does not exist and cannot be deleted.</span>
</div> </div>
<table class="table"> <table class="table">
<thead> <thead>
@ -26,6 +25,7 @@
<th th:text="#{username}">Username</th> <th th:text="#{username}">Username</th>
<th th:text="#{adminUserSettings.roles}">Roles</th> <th th:text="#{adminUserSettings.roles}">Roles</th>
<th th:text="#{adminUserSettings.actions}">Actions</th> <th th:text="#{adminUserSettings.actions}">Actions</th>
<th th:text="#{adminUserSettings.authenticated}">Authenticated</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -37,19 +37,21 @@
<button type="submit" th:text="#{delete}">Delete</button> <button type="submit" th:text="#{delete}">Delete</button>
</form> </form>
</td> </td>
<td th:text="${user.authenticationType}"></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h2 th:text="#{adminUserSettings.addUser}">Add New User</h2> <h2 th:text="#{adminUserSettings.addUser}">Add New User</h2>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'usernameExists' or param.messageType[0] == 'invalidUsername')}" class="alert alert-danger"> <div th:if="${addMessage}" class="alert alert-danger">
<span th:if="${param.messageType[0] == 'usernameExists'}" th:text="#{usernameExistsMessage}">Default message if not found</span> <span th:text="#{${addMessage}}">Default message if not found</span>
<span th:if="${param.messageType[0] == 'invalidUsername'}" th:text="#{invalidUsernameMessage}">Default message if not found</span>
</div> </div>
<form action="/api/v1/user/admin/saveUser" method="post"> <button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
<form id="formsaveuser" action="/api/v1/user/admin/saveUser" method="post">
<div class="mb-3"> <div class="mb-3">
<label for="username" th:text="#{username}">Username</label> <label for="username" th:text="#{username}">Username</label>
<input type="text" class="form-control" name="username" pattern="[a-zA-Z0-9]+" th:title="#{adminUserSettings.usernameInfo}" required> <input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
<span id="usernameError" style="display: none;" th:text="#{invalidUsernameMessage}">Invalid username!</span>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="password" th:text="#{password}">Password</label> <label for="password" th:text="#{password}">Password</label>
@ -73,9 +75,8 @@
<hr /> <hr />
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2> <h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button> <button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
<div th:if="${param.messageType != null and param.messageType.size() > 0 and (param.messageType[0] == 'userNotFound' or param.messageType[0] == 'downgradeCurrentUser')}" class="alert alert-danger"> <div th:if="${changeMessage}" class="alert alert-danger">
<span th:if="${param.messageType[0] == 'userNotFound'}" th:text="#{userNotFoundMessage}">Username not found</span> <span th:text="#{${changeMessage}}">Default message if not found</span>
<span th:if="${param.messageType[0] == 'downgradeCurrentUser'}" th:text="#{downgradeCurrentUserMessage}">Cannot downgrade current user's role</span>
</div> </div>
<form action="/api/v1/user/admin/changeRole" method="post"> <form action="/api/v1/user/admin/changeRole" method="post">
<div class="mb-3"> <div class="mb-3">
@ -101,9 +102,55 @@
</div> </div>
</div> </div>
<script th:inline="javascript"> <script th:inline="javascript">
jQuery.validator.addMethod("usernamePattern", function(value, element) {
return this.optional(element) || /^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$|^(?=.{1,64}@)[A-Za-z0-9]+(\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$/.test(value);
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
$(document).ready(function() { $(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip() $('[data-toggle="tooltip"]').tooltip();
})
$('#formsaveuser').validate({
rules: {
username: {
required: true,
usernamePattern: true
},
password: {
required: true
}
},
messages: {
username: {
usernamePattern: /*[[#{invalidUsernameMessage}]]*/ "Invalid username format"
},
},
errorPlacement: function(error, element) {
if (element.attr("name") === "username") {
$("#usernameError").text(error.text()).show();
} else {
error.insertAfter(element);
}
},
success: function(label, element) {
if ($(element).attr("name") === "username") {
$("#usernameError").hide();
}
}
});
$('#username').on('input', function() {
var usernameInput = $(this);
var isValid = usernameInput[0].checkValidity();
var errorSpan = $('#usernameError');
if (isValid) {
usernameInput.removeClass('invalid').addClass('valid');
errorSpan.hide();
} else {
usernameInput.removeClass('valid').addClass('invalid');
errorSpan.show();
}
});
});
</script> </script>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block> <th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div> </div>

View File

@ -17,18 +17,9 @@
<!-- User Settings Title --> <!-- User Settings Title -->
<h2 class="text-center" th:text="#{changeCreds.header}">User Settings</h2> <h2 class="text-center" th:text="#{changeCreds.header}">User Settings</h2>
<hr> <hr>
<th:block th:if="${param.messageType != null and param.messageType.size() > 0}"> <th:block th:if="${messageType}">
<div th:if="${param.messageType[0] == 'notAuthenticated'}" class="alert alert-danger"> <div class="alert alert-danger">
<span th:text="#{notAuthenticatedMessage}">Default message if not found</span> <span th:text="#{${messageType}}">Default message if not found</span>
</div>
<div th:if="${param.messageType[0] == 'userNotFound'}" class="alert alert-danger">
<span th:text="#{userNotFoundMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType[0] == 'incorrectPassword'}" class="alert alert-danger">
<span th:text="#{incorrectPasswordMessage}">Default message if not found</span>
</div>
<div th:if="${param.messageType[0] == 'usernameExists'}" class="alert alert-danger">
<span th:text="#{usernameExistsMessage}">Default message if not found</span>
</div> </div>
</th:block> </th:block>
<!-- At the top of the user settings --> <!-- At the top of the user settings -->

View File

@ -1,174 +1,170 @@
<th:block th:fragment="head"> <th:block th:fragment="head">
<!-- Title --> <!-- Title -->
<title th:text="${@appName} + (${title} != null and ${title} != '' ? ' - ' + ${title} : '')"></title> <title th:text="${@appName} + (${title} != null and ${title} != '' ? ' - ' + ${title} : '')"></title>
<!-- Metadata --> <!-- Metadata -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="description" th:content="${@appName} + (${header} != null and ${header} != '' ? ' - ' + ${header} : '')"> <meta name="description" th:content="${@appName} + (${header} != null and ${header} != '' ? ' - ' + ${header} : '')">
<meta name="msapplication-TileColor" content="#2d89ef"> <meta name="msapplication-TileColor" content="#2d89ef">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Icons --> <!-- Icons -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=2"> <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png?v=2">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png?v=2"> <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png?v=2">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=2"> <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png?v=2">
<link rel="manifest" href="/site.webmanifest?v=2"> <link rel="manifest" href="site.webmanifest?v=2">
<link rel="mask-icon" href="/safari-pinned-tab.svg?v=2" color="#ca2b2a"> <link rel="mask-icon" href="safari-pinned-tab.svg?v=2" color="#ca2b2a">
<link rel="shortcut icon" href="/favicon.ico?v=2"> <link rel="shortcut icon" href="favicon.ico?v=2">
<meta name="apple-mobile-web-app-title" content="Stirling PDF"> <meta name="apple-mobile-web-app-title" content="Stirling PDF">
<meta name="application-name" content="Stirling PDF"> <meta name="application-name" content="Stirling PDF">
<meta name="msapplication-TileColor" content="#00aba9"> <meta name="msapplication-TileColor" content="#00aba9">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<!-- jQuery --> <!-- jQuery -->
<script src="js/thirdParty/jquery.min.js"></script> <script src="js/thirdParty/jquery.min.js"></script>
<script src="js/thirdParty/jszip.min.js"></script> <script src="js/thirdParty/jquery.validate.min.js"></script>
<script src="js/thirdParty/jszip.min.js"></script>
<!-- Bootstrap --> <!-- Bootstrap -->
<script src="js/thirdParty/popper.min.js"></script> <script src="js/thirdParty/popper.min.js"></script>
<script src="js/thirdParty/bootstrap.min.js"></script> <script src="js/thirdParty/bootstrap.min.js"></script>
<link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap.min.css">
<!-- Bootstrap Icons --> <!-- Bootstrap Icons -->
<link rel="stylesheet" href="css/bootstrap-icons.min.css"> <link rel="stylesheet" href="css/bootstrap-icons.min.css">
<!-- PDF.js --> <!-- PDF.js -->
<script th:src="@{pdfjs/pdf.js}"></script> <script th:src="@{pdfjs/pdf.js}"></script>
<!-- PDF-Lib --> <!-- PDF-Lib -->
<script src="js/thirdParty/pdf-lib.min.js"></script> <script src="js/thirdParty/pdf-lib.min.js"></script>
<!-- Custom --> <!-- Custom -->
<link rel="stylesheet" href="css/general.css"> <link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" th:href="@{css/theme/theme.css}"> <link rel="stylesheet" th:href="@{css/theme/theme.css}">
<link rel="stylesheet" th:href="@{css/theme/componentes.css}"> <link rel="stylesheet" th:href="@{css/theme/componentes.css}">
<link rel="stylesheet" th:href="@{css/theme/theme.light.css}" id="light-mode-styles"> <link rel="stylesheet" th:href="@{css/theme/theme.light.css}" id="light-mode-styles">
<link rel="stylesheet" th:href="@{css/theme/theme.dark.css}" id="dark-mode-styles"> <link rel="stylesheet" th:href="@{css/theme/theme.dark.css}" id="dark-mode-styles">
<link rel="stylesheet" th:href="@{css/rainbow-mode.css}" id="rainbow-mode-styles" disabled> <link rel="stylesheet" th:href="@{css/rainbow-mode.css}" id="rainbow-mode-styles" disabled>
<link rel="stylesheet" href="css/tab-container.css"> <link rel="stylesheet" href="css/tab-container.css">
<link rel="stylesheet" href="css/navbar.css"> <link rel="stylesheet" href="css/navbar.css">
<link rel="stylesheet" th:href="@{/css/error.css}" th:if="${error}"> <link rel="stylesheet" th:href="@{/css/error.css}" th:if="${error}">
<link rel="stylesheet" href="css/home.css" th:if="${currentPage == 'home'}"> <link rel="stylesheet" href="css/home.css" th:if="${currentPage == 'home'}">
<link rel="stylesheet" href="css/account.css" th:if="${currentPage == 'account'}"> <link rel="stylesheet" href="css/account.css" th:if="${currentPage == 'account'}">
<link rel="stylesheet" href="css/licenses.css" th:if="${currentPage == 'licenses'}"> <link rel="stylesheet" href="css/licenses.css" th:if="${currentPage == 'licenses'}">
<link rel="stylesheet" href="css/multi-tool.css" th:if="${currentPage == 'multi-tool'}"> <link rel="stylesheet" href="css/multi-tool.css" th:if="${currentPage == 'multi-tool'}">
<link rel="stylesheet" href="css/rotate-pdf.css" th:if="${currentPage == 'rotate-pdf'}"> <link rel="stylesheet" href="css/rotate-pdf.css" th:if="${currentPage == 'rotate-pdf'}">
<link rel="stylesheet" href="css/stamp.css" th:if="${currentPage == 'stamp'}"> <link rel="stylesheet" href="css/stamp.css" th:if="${currentPage == 'stamp'}">
<link rel="stylesheet" href="css/fileSelect.css"> <link rel="stylesheet" href="css/fileSelect.css">
<link rel="stylesheet" href="css/footer.css"> <link rel="stylesheet" href="css/footer.css">
<script src="js/thirdParty/fontfaceobserver.standalone.js"></script> <script src="js/thirdParty/fontfaceobserver.standalone.js"></script>
<!-- Google MD Icons --> <!-- Google MD Icons -->
<link rel="stylesheet" href="css/theme/font.css"> <link rel="stylesheet" href="css/theme/font.css">
<!-- Help Modal --> <!-- Help Modal -->
<link rel="stylesheet" href="css/errorBanner.css"> <link rel="stylesheet" href="css/errorBanner.css">
<script src="js/cacheFormInputs.js"></script>
<script src="js/tab-container.js"></script>
<script src="js/darkmode.js"></script>
</th:block>
<script src="js/cacheFormInputs.js"></script>
<script src="js/tab-container.js"></script>
<script src="js/darkmode.js"></script>
</th:block>
<th:block th:fragment="game"> <th:block th:fragment="game">
<dialog id="game-container-wrapper" class="game-container-wrapper" data-bs-modal> <dialog id="game-container-wrapper" class="game-container-wrapper" data-bs-modal>
<script th:inline="javascript"> <script th:inline="javascript">
console.log("loaded game"); console.log("loaded game");
$(document).ready(function () { $(document).ready(function() {
// Find the file input within the form // Find the file input within the form
var fileInput = $('input[type="file"]'); var fileInput = $('input[type="file"]');
// Find the closest enclosing form of the file input // Find the closest enclosing form of the file input
var form = fileInput.closest('form'); var form = fileInput.closest('form');
// Find the submit button within the form // Find the submit button within the form
var submitButton = form.find('button[type="submit"], input[type="submit"]'); var submitButton = form.find('button[type="submit"], input[type="submit"]');
const boredWaitingText = /*[[#{bored}]]*/ 'Bored Waiting?'; const boredWaitingText = /*[[#{bored}]]*/ 'Bored Waiting?';
const downloadCompleteText = /*[[#{downloadComplete}]]*/ 'Download Complete'; const downloadCompleteText = /*[[#{downloadComplete}]]*/ 'Download Complete';
window.downloadCompleteText = downloadCompleteText; window.downloadCompleteText = downloadCompleteText;
// Create the 'show-game-btn' button // Create the 'show-game-btn' button
var gameButton = $('<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">' + boredWaitingText + '</button><br><br>'); var gameButton = $('<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">' + boredWaitingText + '</button><br><br>');
// Insert the 'show-game-btn' just above the submit button // Insert the 'show-game-btn' just above the submit button
submitButton.before(gameButton); submitButton.before(gameButton);
function loadGameScript(callback) { function loadGameScript(callback) {
console.log('loadGameScript called'); console.log('loadGameScript called');
const script = document.createElement('script'); const script = document.createElement('script');
script.src = 'js/game.js'; script.src = 'js/game.js';
script.onload = callback; script.onload = callback;
document.body.appendChild(script); document.body.appendChild(script);
}
let gameScriptLoaded = false;
const gameDialog = document.getElementById('game-container-wrapper');
$('#show-game-btn').on('click', function () {
console.log('Show game button clicked');
if (!gameScriptLoaded) {
console.log('Show game button load');
loadGameScript(function () {
console.log('Game script loaded');
window.initializeGame();
gameScriptLoaded = true;
});
} else {
window.resetGame();
}
gameDialog.showModal();
});
gameDialog.addEventListener("click", e => {
const dialogDimensions = gameDialog.getBoundingClientRect()
if (
e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
gameDialog.close();
} }
let gameScriptLoaded = false;
const gameDialog = document.getElementById('game-container-wrapper');
$('#show-game-btn').on('click', function() {
console.log('Show game button clicked');
if (!gameScriptLoaded) {
console.log('Show game button load');
loadGameScript(function() {
console.log('Game script loaded');
window.initializeGame();
gameScriptLoaded = true;
});
} else {
window.resetGame();
}
gameDialog.showModal();
});
gameDialog.addEventListener("click", e => {
const dialogDimensions = gameDialog.getBoundingClientRect()
if (
e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
gameDialog.close();
}
})
}) })
}) </script>
</script> <div id="game-container">
<div id="game-container"> <div id="lives">Lives: 3</div>
<div id="lives">Lives: 3</div> <div id="score">Score: 0</div>
<div id="score">Score: 0</div> <div id="high-score">High Score: 0</div>
<div id="high-score">High Score: 0</div> <div id="level">Level: 1</div>
<div id="level">Level: 1</div> <img src="favicon.svg" class="player" id="player" alt="favicon">
<img src="favicon.svg" class="player" id="player" alt="favicon">
</div>
<link rel="stylesheet" href="css/game.css">
</dialog>
</th:block>
<th:block th:fragment="fileSelector(name, multiple)"
th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
<script th:inline="javascript">
const pdfPasswordPrompt = /*[[#{error.pdfPassword}]]*/ '';
const multiple = [[${ multiple }]] || false;
const remoteCall = [[${ remoteCall }]] || true;
</script>
<script src="js/downloader.js"></script>
<div class="custom-file-chooser"
th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
<div class="mb-3">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple
th:required="${notRequired} ? null : 'required'">
</div>
<div class="selected-files"></div>
</div>
<div class="progressBarContainer" style="display: none; position: relative;">
<div class="progress" style="height: 1rem;">
<div class="progressBar progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
<span class="visually-hidden">Loading...</span>
</div> </div>
</div> <link rel="stylesheet" href="css/game.css">
</div> </dialog>
<script src="js/fileInput.js"></script> </th:block>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
<script th:inline="javascript">
const pdfPasswordPrompt = /*[[#{error.pdfPassword}]]*/ '';
const multiple = [[${multiple}]] || false;
const remoteCall = [[${remoteCall}]] || true;
</script>
<script src="js/downloader.js"></script>
<div class="custom-file-chooser" th:attr="data-bs-unique-id=${name}, data-bs-element-id=${name+'-input'}, data-bs-files-selected=#{filesSelected}, data-bs-pdf-prompt=#{pdfPrompt}">
<div class="mb-3">
<input type="file" class="form-control" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple th:required="${notRequired} ? null : 'required'">
</div>
<div class="selected-files"></div>
</div>
<div class="progressBarContainer" style="display: none; position: relative;">
<div class="progress" style="height: 1rem;">
<div class="progressBar progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
<script src="js/fileInput.js"></script>
</th:block> </th:block>

View File

@ -112,25 +112,32 @@
} }
} }
</script> </script>
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div> <div class="text-center">
<div th:if="${param.messageType != null and param.messageType.size() > 0 and param.messageType[0] == 'credsUpdated'}" class="alert alert-success"> <img class="mb-4" src="favicon.svg?v=2" alt="favicon" width="144" height="144">
<span th:text="#{changedCredsMessage}">Default message if not found</span>
</div> <h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
<form th:action="@{login}" method="post"> <div th:if="${oAuth2Enabled}">
<div class="text-center">
<img class="mb-4" src="favicon.svg?v=2" alt="" width="144" height="144">
<h1 class="h1 mb-3 fw-normal" th:text="${@appName}">Stirling-PDF</h1>
<div th:if="${oAuth2Enabled}">
<a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a> <a href="oauth2/authorization/oidc" class="w-100 btn btn-lg btn-primary" th:text="#{login.ssoSignIn}">Login Via SSO</a>
<div class="text-danger text-center"> <br>
<div th:if="${error == 'oauth2AutoCreateDisabled'}" th:text="#{login.oauth2AutoCreateDisabled}">OAUTH2 Auto-Create User Disabled.</div> <br>
<div th:if="${erroroauth}" class="alert alert-danger text-center">
<div th:if="${erroroauth}" th:text="#{${erroroauth}}">OAuth2: Error Message</div>
</div> </div>
<hr /> <hr />
</div> </div>
<h2 class="h5 mb-3 fw-normal" th:text="#{login.signinTitle}">Please sign in</h2>
<div th:if="${error}" class="alert alert-danger text-danger text-center">
<div th:if="${error}" th:text="#{${error}}">OAuth2: Error Message</div>
</div> </div>
<div class="text-danger text-center">
<div th:if="${logoutMessage}" class="alert alert-success" th:text="${logoutMessage}"></div>
<div th:if="${messageType}" class="alert alert-success">
<span th:text="#{${messageType}}">Default message if not found</span>
</div>
</div>
</div>
<form th:action="@{login}" method="post">
<h2 class="h5 mb-3 fw-normal" th:text="#{login.signinTitle}">Please sign in</h2>
<div class="form-floating"> <div class="form-floating">
<input type="text" class="form-control" id="username" name="username" placeholder="admin"> <input type="text" class="form-control" id="username" name="username" placeholder="admin">
<label for="username" th:text="#{username}">Username</label> <label for="username" th:text="#{username}">Username</label>
@ -155,15 +162,11 @@
<div class="dropdown-menu" aria-labelledby="languageDropdown"> <div class="dropdown-menu" aria-labelledby="languageDropdown">
<!-- Here's where the fragment will be included --> <!-- Here's where the fragment will be included -->
<div class="scrollable-y"> <div class="scrollable-y">
<th:block th:replace="~{fragments/languages :: langs}"></th:block> <th:block th:replace="~{fragments/languages :: langs}"></th:block>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="text-danger text-center">
<div th:if="${error == 'badcredentials'}" th:text="#{login.invalid}">Invalid username or password.</div>
<div th:if="${error == 'locked'}" th:text="#{login.locked}">Your account has been locked. </div>
</div>
</main> </main>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block> <th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div> </div>