mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-08-06 13:48:58 +02:00
Merge remote-tracking branch 'origin/main' into team
This commit is contained in:
commit
255c99b3de
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
@ -24,4 +24,4 @@ jobs:
|
|||||||
- name: "Checkout Repository"
|
- name: "Checkout Repository"
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: "Dependency Review"
|
- name: "Dependency Review"
|
||||||
uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0
|
uses: actions/dependency-review-action@38ecb5b593bf0eb19e335c03f97670f792489a8b # v4.7.0
|
||||||
|
@ -541,7 +541,7 @@ This would generate n entries of tr for each person in exampleData
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" th:href="@{/new-feature}">New Feature</a>
|
<a class="nav-link" th:href="@{'/new-feature'}">New Feature</a>
|
||||||
</li>
|
</li>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ Visit our comprehensive documentation at [docs.stirlingpdf.com](https://docs.sti
|
|||||||
|
|
||||||
## Supported Languages
|
## Supported Languages
|
||||||
|
|
||||||
Stirling-PDF currently supports 39 languages!
|
Stirling-PDF currently supports 40 languages!
|
||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
@ -156,7 +156,7 @@ Stirling-PDF currently supports 39 languages!
|
|||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
|
| Malayalam (മലയാളം) (ml_ML) |  |
|
||||||
|
|
||||||
## Stirling PDF Enterprise
|
## Stirling PDF Enterprise
|
||||||
|
|
||||||
|
@ -479,7 +479,7 @@ dependencies {
|
|||||||
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||||
|
|
||||||
// Batik
|
// Batik
|
||||||
implementation "org.apache.xmlgraphics:batik-all:1.18"
|
implementation "org.apache.xmlgraphics:batik-all:1.19"
|
||||||
|
|
||||||
// TwelveMonkeys
|
// TwelveMonkeys
|
||||||
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
|
runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion"
|
||||||
@ -527,7 +527,7 @@ dependencies {
|
|||||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||||
implementation "io.micrometer:micrometer-core:1.14.6"
|
implementation "io.micrometer:micrometer-core:1.14.7"
|
||||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||||
implementation "org.commonmark:commonmark:0.24.0"
|
implementation "org.commonmark:commonmark:0.24.0"
|
||||||
|
@ -33,7 +33,7 @@ public class EEAppConfig {
|
|||||||
public boolean runningProOrHigher() {
|
public boolean runningProOrHigher() {
|
||||||
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "license")
|
@Bean(name = "license")
|
||||||
public String licenseType() {
|
public String licenseType() {
|
||||||
return licenseKeyChecker.getPremiumLicenseEnabledResult().name();
|
return licenseKeyChecker.getPremiumLicenseEnabledResult().name();
|
||||||
|
@ -36,7 +36,7 @@ public class AppConfig {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
|
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
|
||||||
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
|
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
|
||||||
@ -197,12 +197,12 @@ public class AppConfig {
|
|||||||
public String uuid() {
|
public String uuid() {
|
||||||
return applicationProperties.getAutomaticallyGenerated().getUUID();
|
return applicationProperties.getAutomaticallyGenerated().getUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "disablePixel")
|
@Bean(name = "disablePixel")
|
||||||
public boolean disablePixel() {
|
public boolean disablePixel() {
|
||||||
return Boolean.getBoolean(env.getProperty("DISABLE_PIXEL"));
|
return Boolean.getBoolean(env.getProperty("DISABLE_PIXEL"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "machineType")
|
@Bean(name = "machineType")
|
||||||
public String determineMachineType() {
|
public String determineMachineType() {
|
||||||
try {
|
try {
|
||||||
|
@ -73,7 +73,7 @@ public class InitialSetup {
|
|||||||
// Initialize Terms and Conditions
|
// Initialize Terms and Conditions
|
||||||
String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
|
String termsUrl = applicationProperties.getLegal().getTermsAndConditions();
|
||||||
if (StringUtils.isEmpty(termsUrl)) {
|
if (StringUtils.isEmpty(termsUrl)) {
|
||||||
String defaultTermsUrl = "https://www.stirlingpdf.com/terms-and-conditions";
|
String defaultTermsUrl = "https://www.stirlingpdf.com/terms";
|
||||||
GeneralUtils.saveKeyToSettings("legal.termsAndConditions", defaultTermsUrl);
|
GeneralUtils.saveKeyToSettings("legal.termsAndConditions", defaultTermsUrl);
|
||||||
applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
|
applicationProperties.getLegal().setTermsAndConditions(defaultTermsUrl);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ public class MetricsConfig {
|
|||||||
return new MeterFilter() {
|
return new MeterFilter() {
|
||||||
@Override
|
@Override
|
||||||
public MeterFilterReply accept(Meter.Id id) {
|
public MeterFilterReply accept(Meter.Id id) {
|
||||||
if (id.getName().equals("http.requests")) {
|
if ("http.requests".equals(id.getName())) {
|
||||||
return MeterFilterReply.NEUTRAL;
|
return MeterFilterReply.NEUTRAL;
|
||||||
}
|
}
|
||||||
return MeterFilterReply.DENY;
|
return MeterFilterReply.DENY;
|
||||||
|
@ -5,7 +5,9 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.models.Components;
|
import io.swagger.v3.oas.models.Components;
|
||||||
import io.swagger.v3.oas.models.OpenAPI;
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Contact;
|
||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.info.License;
|
||||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
|
||||||
@ -31,14 +33,25 @@ public class OpenApiConfig {
|
|||||||
// default version if all else fails
|
// default version if all else fails
|
||||||
version = "1.0.0";
|
version = "1.0.0";
|
||||||
}
|
}
|
||||||
|
Info info =
|
||||||
|
new Info()
|
||||||
|
.title(DEFAULT_TITLE)
|
||||||
|
.version(version)
|
||||||
|
.license(
|
||||||
|
new License()
|
||||||
|
.name("MIT")
|
||||||
|
.url(
|
||||||
|
"https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/refs/heads/main/LICENSE")
|
||||||
|
.identifier("MIT"))
|
||||||
|
.termsOfService("https://www.stirlingpdf.com/terms")
|
||||||
|
.contact(
|
||||||
|
new Contact()
|
||||||
|
.name("Stirling Software")
|
||||||
|
.url("https://www.stirlingpdf.com")
|
||||||
|
.email("contact@stirlingpdf.com"))
|
||||||
|
.description(DEFAULT_DESCRIPTION);
|
||||||
if (!applicationProperties.getSecurity().getEnableLogin()) {
|
if (!applicationProperties.getSecurity().getEnableLogin()) {
|
||||||
return new OpenAPI()
|
return new OpenAPI().components(new Components()).info(info);
|
||||||
.components(new Components())
|
|
||||||
.info(
|
|
||||||
new Info()
|
|
||||||
.title(DEFAULT_TITLE)
|
|
||||||
.version(version)
|
|
||||||
.description(DEFAULT_DESCRIPTION));
|
|
||||||
} else {
|
} else {
|
||||||
SecurityScheme apiKeyScheme =
|
SecurityScheme apiKeyScheme =
|
||||||
new SecurityScheme()
|
new SecurityScheme()
|
||||||
@ -47,11 +60,7 @@ public class OpenApiConfig {
|
|||||||
.name("X-API-KEY");
|
.name("X-API-KEY");
|
||||||
return new OpenAPI()
|
return new OpenAPI()
|
||||||
.components(new Components().addSecuritySchemes("apiKey", apiKeyScheme))
|
.components(new Components().addSecuritySchemes("apiKey", apiKeyScheme))
|
||||||
.info(
|
.info(info)
|
||||||
new Info()
|
|
||||||
.title(DEFAULT_TITLE)
|
|
||||||
.version(version)
|
|
||||||
.description(DEFAULT_DESCRIPTION))
|
|
||||||
.addSecurityItem(new SecurityRequirement().addList("apiKey"));
|
.addSecurityItem(new SecurityRequirement().addList("apiKey"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ premium:
|
|||||||
appId: ''
|
appId: ''
|
||||||
|
|
||||||
mail:
|
mail:
|
||||||
enabled: true # set to 'true' to enable sending emails
|
enabled: false # set to 'true' to enable sending emails
|
||||||
host: smtp.example.com # SMTP server hostname
|
host: smtp.example.com # SMTP server hostname
|
||||||
port: 587 # SMTP server port
|
port: 587 # SMTP server port
|
||||||
username: '' # SMTP server username
|
username: '' # SMTP server username
|
||||||
@ -85,7 +85,7 @@ mail:
|
|||||||
from: '' # sender email address
|
from: '' # sender email address
|
||||||
|
|
||||||
legal:
|
legal:
|
||||||
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
termsAndConditions: https://www.stirlingpdf.com/terms # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
||||||
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
|
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
|
||||||
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
|
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
|
||||||
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
|
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
|
||||||
@ -113,11 +113,11 @@ system:
|
|||||||
name: postgres # set the name of your database. Should match the name of the database you create
|
name: postgres # set the name of your database. Should match the name of the database you create
|
||||||
customPaths:
|
customPaths:
|
||||||
pipeline:
|
pipeline:
|
||||||
watchedFoldersDir: '' #Defaults to /pipeline/watchedFolders
|
watchedFoldersDir: '' # Defaults to /pipeline/watchedFolders
|
||||||
finishedFoldersDir: '' #Defaults to /pipeline/finishedFolders
|
finishedFoldersDir: '' # Defaults to /pipeline/finishedFolders
|
||||||
operations:
|
operations:
|
||||||
weasyprint: '' #Defaults to /opt/venv/bin/weasyprint
|
weasyprint: '' # Defaults to /opt/venv/bin/weasyprint
|
||||||
unoconvert: '' #Defaults to /opt/venv/bin/unoconvert
|
unoconvert: '' # Defaults to /opt/venv/bin/unoconvert
|
||||||
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
|
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||||
<head>
|
<head>
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{adminUserSettings.title}, header=#{adminUserSettings.header})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{adminUserSettings.title}, header=#{adminUserSettings.header})}"></th:block>
|
||||||
<link rel="stylesheet" th:href="@{/css/modern-tables.css}">
|
|
||||||
<style>
|
<style>
|
||||||
.active-user {
|
.active-user {
|
||||||
color: var(--md-sys-color-tertiary);
|
color: green;
|
||||||
font-weight: 600;
|
text-shadow: 0 0 5px green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-overflow {
|
.text-overflow {
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow:ellipsis;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@ -23,354 +22,215 @@
|
|||||||
<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>
|
||||||
<div class="data-container">
|
<div class="container">
|
||||||
<div class="data-panel">
|
<div class="row justify-content-center">
|
||||||
<div class="data-header">
|
<div class="col-md-9 bg-card">
|
||||||
<h1 class="data-title">
|
<div class="tool-header">
|
||||||
<span class="data-icon">
|
<span class="material-symbols-rounded tool-header-icon organize">manage_accounts</span>
|
||||||
<span class="material-symbols-rounded">manage_accounts</span>
|
<span class="tool-header-text" th:text="#{adminUserSettings.header}">Admin User Control Settings</span>
|
||||||
</span>
|
</div>
|
||||||
<span th:text="#{adminUserSettings.header}">Admin User Control Settings</span>
|
|
||||||
</h1>
|
<!-- User Settings Title -->
|
||||||
</div>
|
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
|
||||||
|
<a href="#"
|
||||||
<div class="data-body">
|
th:data-bs-toggle="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : 'modal'"
|
||||||
<!-- User Stats Banner -->
|
th:data-bs-target="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : '#addUserModal'"
|
||||||
<div class="data-panel data-mb-3" style="background-color: var(--md-sys-color-primary-container);">
|
th:class="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
|
||||||
<div class="data-body" style="padding: 1.25rem;">
|
th:title="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
|
||||||
<div style="display: flex; flex-wrap: wrap; justify-content: space-around; align-items: center; gap: 1.5rem;">
|
<span class="material-symbols-rounded">person_add</span>
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
||||||
<span class="material-symbols-rounded" style="font-size: 2.25rem; color: var(--md-sys-color-primary);">
|
</a>
|
||||||
group
|
|
||||||
</span>
|
<a href="#"
|
||||||
<div>
|
data-bs-toggle="modal"
|
||||||
<div style="color: var(--md-sys-color-primary); font-size: 0.875rem; font-weight: 500;" th:text="#{adminUserSettings.totalUsers}">Total Users</div>
|
data-bs-target="#changeUserRoleModal"
|
||||||
<div style="color: var(--md-sys-color-primary); font-size: 1.5rem; font-weight: 700;">
|
class="btn btn-outline-success"
|
||||||
<span th:text="${totalUsers}"></span>
|
th:title="#{adminUserSettings.changeUserRole}">
|
||||||
<span th:if="${@runningProOrHigher}" th:text="'/' + ${maxPaidUsers}" style="font-size: 1rem;"></span>
|
<span class="material-symbols-rounded">edit</span>
|
||||||
</div>
|
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
||||||
</div>
|
</a>
|
||||||
</div>
|
|
||||||
|
<a th:href="@{'/usage'}" th:if="${@runningEE}"
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
class="btn btn-outline-success"
|
||||||
<span class="material-symbols-rounded" style="font-size: 2.25rem; color: var(--md-sys-color-primary);">
|
th:title="#{adminUserSettings.usage}">
|
||||||
check_circle
|
<span class="material-symbols-rounded">analytics</span>
|
||||||
</span>
|
<span th:text="#{adminUserSettings.usage}">Usage Statistics</span>
|
||||||
<div>
|
</a>
|
||||||
<div style="color: var(--md-sys-color-primary); font-size: 0.875rem; font-weight: 500;" th:text="#{adminUserSettings.activeUsers}">Active Users</div>
|
|
||||||
<div style="color: var(--md-sys-color-primary); font-size: 1.5rem; font-weight: 700;" th:text="${activeUsers}"></div>
|
<div class="my-4">
|
||||||
</div>
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong>
|
||||||
</div>
|
<span th:text="${totalUsers}"></span>
|
||||||
|
<span th:if="${@runningProOrHigher}" th:text="'/'+${maxPaidUsers}"></span>
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
|
||||||
<span class="material-symbols-rounded" style="font-size: 2.25rem; color: var(--md-sys-color-primary);">
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
||||||
person_off
|
<span th:text="${activeUsers}"></span>
|
||||||
</span>
|
|
||||||
<div>
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users:</strong>
|
||||||
<div style="color: var(--md-sys-color-primary); font-size: 0.875rem; font-weight: 500;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users</div>
|
<span th:text="${disabledUsers}"></span>
|
||||||
<div style="color: var(--md-sys-color-primary); font-size: 1.5rem; font-weight: 700;" th:text="${disabledUsers}"></div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
<div th:if="${addMessage}" class="p-3" style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
|
||||||
|
<div class="alert alert-danger mb-auto">
|
||||||
|
<span th:text="#{${addMessage}}">Default message if not found</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div th:if="${changeMessage}" class="p-3" style="background: var(--md-sys-color-outline-variant);border-radius: 2rem; text-align: center;">
|
||||||
<!-- Alert Messages -->
|
<div class="alert alert-danger mb-auto">
|
||||||
<div th:if="${addMessage}" class="alert alert-danger data-mb-3">
|
<span th:text="#{${changeMessage}}">Default message if not found</span>
|
||||||
<span th:text="#{${addMessage}}">Default message if not found</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div th:if="${deleteMessage}" class="alert alert-danger">
|
||||||
<div th:if="${changeMessage}" class="alert alert-danger data-mb-3">
|
|
||||||
<span th:text="#{${changeMessage}}">Default message if not found</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div th:if="${deleteMessage}" class="alert alert-danger data-mb-3">
|
|
||||||
<span th:text="#{${deleteMessage}}">Default message if not found</span>
|
<span th:text="#{${deleteMessage}}">Default message if not found</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bg-card mt-3 mb-3 table-responsive">
|
||||||
<!-- Admin Actions -->
|
<table class="table table-striped table-hover">
|
||||||
<div class="data-section-title">User Management</div>
|
|
||||||
<div class="data-actions data-mb-3">
|
|
||||||
<button
|
|
||||||
th:data-bs-toggle="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : 'modal'"
|
|
||||||
th:data-bs-target="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : '#addUserModal'"
|
|
||||||
th:class="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? 'data-btn data-btn-danger' : 'data-btn data-btn-primary'"
|
|
||||||
th:title="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
|
|
||||||
<span class="material-symbols-rounded">person_add</span>
|
|
||||||
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<a href="/teams" class="data-btn data-btn-secondary" th:title="#{adminUserSettings.teams}">
|
|
||||||
<span class="material-symbols-rounded">group</span>
|
|
||||||
<span th:text="#{adminUserSettings.teams}">Manage Teams</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<button
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#changeUserRoleModal"
|
|
||||||
class="data-btn data-btn-secondary"
|
|
||||||
th:title="#{adminUserSettings.changeUserRole}">
|
|
||||||
<span class="material-symbols-rounded">edit</span>
|
|
||||||
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<a href="/usage" th:if="${@runningEE}" class="data-btn data-btn-secondary" th:title="#{adminUserSettings.usage}">
|
|
||||||
<span class="material-symbols-rounded">analytics</span>
|
|
||||||
<span th:text="#{adminUserSettings.usage}">Usage Statistics</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Users Table -->
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="data-table">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">#</th>
|
<th scope="col">#</th>
|
||||||
<th scope="col" th:title="#{username}" th:text="#{username}">Username</th>
|
<th scope="col" th:title="#{username}" th:text="#{username}">Username</th>
|
||||||
<th scope="col" th:title="#{adminUserSettings.team}" th:text="#{adminUserSettings.team}">Team</th>
|
|
||||||
<th scope="col" th:title="#{adminUserSettings.roles}" th:text="#{adminUserSettings.roles}">Roles</th>
|
<th scope="col" th:title="#{adminUserSettings.roles}" th:text="#{adminUserSettings.roles}">Roles</th>
|
||||||
<th scope="col" th:title="#{adminUserSettings.authenticated}" class="text-overflow" th:text="#{adminUserSettings.authenticated}">Authenticated</th>
|
<th scope="col" th:title="#{adminUserSettings.authenticated}" class="text-overflow" th:text="#{adminUserSettings.authenticated}">Authenticated</th>
|
||||||
<th scope="col" th:title="#{adminUserSettings.lastRequest}" class="text-overflow" th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
<th scope="col" th:title="#{adminUserSettings.lastRequest}" class="text-overflow" th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
||||||
<th scope="col" th:title="#{adminUserSettings.actions}" th:text="#{adminUserSettings.actions}">Actions</th>
|
<th scope="col" th:title="#{adminUserSettings.actions}" th:text="#{adminUserSettings.actions}" colspan="2">Actions</th>
|
||||||
|
<!-- <th scope="col"></th> -->
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr th:each="user : ${users}">
|
<tr th:each="user : ${users}">
|
||||||
<td th:text="${user.id}"></td>
|
<th scope="row" style="align-content: center;" th:text="${user.id}"></th>
|
||||||
<td th:text="${user.username}" th:classappend="${userSessions[user.username] ? 'active-user' : ''}"></td>
|
<td style="align-content: center;" th:text="${user.username}" th:classappend="${userSessions[user.username] ? 'active-user' : ''}"></td>
|
||||||
<td th:text="${user.team != null ? user.team.name : '—'}"></td>
|
<td style="align-content: center;" th:text="#{${user.roleName}}"></td>
|
||||||
<td>
|
<td style="align-content: center;" th:text="${user.authenticationType}"></td>
|
||||||
<span class="data-badge" style="background-color: var(--md-sys-color-secondary-container); color: var(--md-sys-color-secondary); padding: 0.25rem 0.5rem; border-radius: 1rem; font-size: 0.875rem; display: inline-flex; align-items: center; gap: 0.25rem;">
|
<td style="align-content: center;" th:text="${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}"></td>
|
||||||
<span class="material-symbols-rounded" style="font-size: 1rem;">shield</span>
|
<td style="align-content: center;">
|
||||||
<span th:text="#{${user.roleName}}">Role</span>
|
<form th:if="${user.username != currentUsername}" th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post" onsubmit="return confirmDeleteUser()">
|
||||||
</span>
|
<button type="submit" th:title="#{adminUserSettings.deleteUser}" class="btn btn-info btn-sm"><span class="material-symbols-rounded">person_remove</span></button>
|
||||||
|
</form>
|
||||||
|
<a th:if="${user.username == currentUsername}" th:title="#{adminUserSettings.editOwnProfil}" th:href="@{'/account'}" class="btn btn-outline-success btn-sm"><span class="material-symbols-rounded">edit</span></a>
|
||||||
</td>
|
</td>
|
||||||
<td th:text="${user.authenticationType}"></td>
|
<td style="align-content: center;">
|
||||||
<td th:text="${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}"></td>
|
<form th:action="@{'/api/v1/user/admin/changeUserEnabled/' + ${user.username}}" method="post" onsubmit="return confirmChangeUserStatus()">
|
||||||
<td>
|
<input type="hidden" name="enabled" th:value="!${user.enabled}" />
|
||||||
<div class="data-action-cell">
|
<button type="submit" th:if="${user.enabled}" th:title="#{adminUserSettings.enabledUser}" class="btn btn-success btn-sm">
|
||||||
<form th:if="${user.username != currentUsername}" th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post" onsubmit="return confirmDeleteUser()" style="display: inline;">
|
<span class="material-symbols-rounded">person</span>
|
||||||
<button type="submit" th:title="#{adminUserSettings.deleteUser}" class="data-icon-btn data-icon-btn-danger">
|
</button>
|
||||||
<span class="material-symbols-rounded">person_remove</span>
|
<button type="submit" th:unless="${user.enabled}" th:title="#{adminUserSettings.disabledUser}" class="btn btn-danger btn-sm">
|
||||||
</button>
|
<span class="material-symbols-rounded">person_off</span>
|
||||||
</form>
|
</button>
|
||||||
|
</form>
|
||||||
<a th:if="${user.username == currentUsername}" th:title="#{adminUserSettings.editOwnProfil}" th:href="@{'/account'}" class="data-icon-btn data-icon-btn-primary">
|
|
||||||
<span class="material-symbols-rounded">edit</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<form th:action="@{'/api/v1/user/admin/changeUserEnabled/' + ${user.username}}" method="post" onsubmit="return confirmChangeUserStatus()" style="display: inline;">
|
|
||||||
<input type="hidden" name="enabled" th:value="!${user.enabled}" />
|
|
||||||
<button type="submit" th:if="${user.enabled}" th:title="#{adminUserSettings.enabledUser}" class="data-icon-btn data-icon-btn-primary">
|
|
||||||
<span class="material-symbols-rounded">person</span>
|
|
||||||
</button>
|
|
||||||
<button type="submit" th:unless="${user.enabled}" th:title="#{adminUserSettings.disabledUser}" class="data-icon-btn data-icon-btn-danger">
|
|
||||||
<span class="material-symbols-rounded">person_off</span>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<p th:if="${!@runningProOrHigher}" th:text="#{enterpriseEdition.ssoAdvert}"></p>
|
||||||
<p th:if="${!@runningProOrHigher}" class="data-mt-3" th:text="#{enterpriseEdition.ssoAdvert}"></p>
|
|
||||||
|
<script th:inline="javascript">
|
||||||
|
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
|
||||||
|
const change_confirm_text = /*[[#{adminUserSettings.confirmChangeUserStatus}]]*/ 'Should the user be disabled/enabled?';
|
||||||
|
function confirmDeleteUser() {
|
||||||
|
return confirm(delete_confirm_text);
|
||||||
|
}
|
||||||
|
function confirmChangeUserStatus() {
|
||||||
|
return confirm(change_confirm_text);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Change User Role Modal -->
|
<!-- change User role Modal start -->
|
||||||
<div class="modal fade" id="changeUserRoleModal" tabindex="-1" aria-labelledby="changeUserRoleModalLabel" aria-hidden="true">
|
<div class="modal fade" id="changeUserRoleModal" tabindex="-1" aria-labelledby="changeUserRoleModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<form th:action="@{'/api/v1/user/admin/changeRole'}" method="post" class="modal-content data-modal">
|
<div class="modal-content">
|
||||||
<div class="data-modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="data-modal-title">
|
<h2 th:text="#{adminUserSettings.changeUserRole}">Change User's Role</h2>
|
||||||
<span class="data-icon">
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||||
<span class="material-symbols-rounded">edit</span>
|
|
||||||
</span>
|
|
||||||
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
|
||||||
</h5>
|
|
||||||
<button type="button" class="data-btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
<span class="material-symbols-rounded">close</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="data-modal-body">
|
<div class="modal-body">
|
||||||
<div class="data-mb-2">
|
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" th:text="#{help}">Help</button>
|
||||||
<button class="data-btn data-btn-secondary" data-toggle="tooltip" data-placement="auto" th:title="#{downgradeCurrentUserLongMessage}" style="padding: 0.25rem 0.5rem;">
|
<form th:action="@{'/api/v1/user/admin/changeRole'}" method="post">
|
||||||
<span class="material-symbols-rounded">help</span>
|
<div class="mb-3">
|
||||||
<span th:text="#{help}">Help</span>
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
</button>
|
<select name="username" class="form-control" required>
|
||||||
</div>
|
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||||
|
<option th:each="user : ${users}" th:if="${user.username != currentUsername}" th:value="${user.username}" th:text="${user.username}">Username</option>
|
||||||
<div class="data-form-group">
|
</select>
|
||||||
<label for="username" class="data-form-label" th:text="#{username}">Username</label>
|
</div>
|
||||||
<select name="username" id="username" class="data-form-control" required>
|
<div class="mb-3">
|
||||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||||
<option th:each="user : ${users}" th:if="${user.username != currentUsername}" th:value="${user.username}" th:text="${user.username}">Username</option>
|
<select name="role" class="form-control" required>
|
||||||
</select>
|
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||||
</div>
|
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
||||||
|
</select>
|
||||||
<div class="data-form-group">
|
</div>
|
||||||
<label for="role" class="data-form-label" th:text="#{adminUserSettings.role}">Role</label>
|
|
||||||
<select name="role" id="role" class="data-form-control" required>
|
<!-- Add other fields as required -->
|
||||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||||
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
</form>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="data-form-group">
|
|
||||||
<label for="team" class="data-form-label" th:text="#{adminUserSettings.team}">Team</label>
|
|
||||||
<select name="teamId" id="team" class="data-form-control" required>
|
|
||||||
<option value="" th:text="#{selectFillter}">-- Select --</option>
|
|
||||||
<option th:each="team : ${teams}" th:value="${team.id}" th:text="${team.name}"></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="data-form-actions">
|
|
||||||
<button type="button" class="data-btn data-btn-secondary" data-bs-dismiss="modal">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
<span th:text="#{cancel}">Cancel</span>
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="data-btn data-btn-primary">
|
|
||||||
<span class="material-symbols-rounded">check</span>
|
|
||||||
<span th:text="#{adminUserSettings.submit}">Save User</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div class="modal-footer"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- change User role Modal end -->
|
||||||
|
|
||||||
<!-- Add User Modal -->
|
<!-- Add User Modal start -->
|
||||||
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<form id="formsaveuser" th:action="@{'/api/v1/user/admin/saveUser'}" method="post" class="modal-content data-modal">
|
<div class="modal-content">
|
||||||
<div class="data-modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="data-modal-title">
|
<h5 class="modal-title" id="addUserModalLabel" th:text="#{adminUserSettings.addUser}">Add New User</h5>
|
||||||
<span class="data-icon">
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||||
<span class="material-symbols-rounded">person_add</span>
|
|
||||||
</span>
|
|
||||||
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
|
||||||
</h5>
|
|
||||||
<button type="button" class="data-btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
<span class="material-symbols-rounded">close</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="data-modal-body">
|
<div class="modal-body">
|
||||||
<div class="data-mb-2">
|
<button class="btn btn-outline-info" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" th:text="#{help}">Help</button>
|
||||||
<button class="data-btn data-btn-secondary" data-toggle="tooltip" data-placement="auto" th:title="#{adminUserSettings.usernameInfo}" style="padding: 0.25rem 0.5rem;">
|
<form id="formsaveuser" th:action="@{'/api/v1/user/admin/saveUser'}" method="post">
|
||||||
<span class="material-symbols-rounded">help</span>
|
<div class="mb-3">
|
||||||
<span th:text="#{help}">Help</span>
|
<label for="username" th:text="#{username}">Username</label>
|
||||||
</button>
|
<input type="text" class="form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
|
||||||
</div>
|
<span id="usernameError" style="display: none;" th:text="#{invalidUsernameMessage}">Invalid username!</span>
|
||||||
|
</div>
|
||||||
<div class="data-form-group">
|
<div class="mb-3" id="passwordContainer">
|
||||||
<label for="username" class="data-form-label" th:text="#{username}">Username</label>
|
<label for="password" th:text="#{password}">Password</label>
|
||||||
<input type="text" class="data-form-control" name="username" id="username" th:title="#{adminUserSettings.usernameInfo}" required>
|
<input type="password" class="form-control" name="password" id="password" required>
|
||||||
<span id="usernameError" style="display: none; color: var(--md-sys-color-error);" th:text="#{invalidUsernameMessage}">Invalid username!</span>
|
</div>
|
||||||
</div>
|
<div class="mb-3">
|
||||||
|
<label for="role" th:text="#{adminUserSettings.role}">Role</label>
|
||||||
<div class="data-form-group" id="passwordContainer">
|
<select name="role" class="form-control" id="role" required>
|
||||||
<label for="password" class="data-form-label" th:text="#{password}">Password</label>
|
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
||||||
<input type="password" class="data-form-control" name="password" id="password" required>
|
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
||||||
</div>
|
</select>
|
||||||
|
</div>
|
||||||
<div class="data-form-group">
|
<div class="mb-3">
|
||||||
<label for="role" class="data-form-label" th:text="#{adminUserSettings.role}">Role</label>
|
<label for="authType">Authentication Type</label>
|
||||||
<select name="role" class="data-form-control" id="role" required>
|
<select id="authType" name="authType" class="form-control" required>
|
||||||
<option value="" disabled selected th:text="#{selectFillter}">-- Select --</option>
|
<option value="web" selected>WEB</option>
|
||||||
<option th:each="roleDetail : ${roleDetails}" th:value="${roleDetail.key}" th:text="#{${roleDetail.value}}">Role</option>
|
<option value="sso">SSO</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-check mb-3" id="checkboxContainer">
|
||||||
<div class="data-form-group">
|
|
||||||
<label for="team" class="data-form-label" th:text="#{adminUserSettings.team}">Team</label>
|
|
||||||
<select name="teamId" class="data-form-control" required>
|
|
||||||
<option value="" th:text="#{selectFillter}">-- Select --</option>
|
|
||||||
<option th:each="team : ${teams}" th:value="${team.id}" th:text="${team.name}"></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="data-form-group">
|
|
||||||
<label for="authType" class="data-form-label">Authentication Type</label>
|
|
||||||
<select id="authType" name="authType" class="data-form-control" required>
|
|
||||||
<option value="web" selected>WEB</option>
|
|
||||||
<option value="sso">SSO</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="data-form-group" id="checkboxContainer">
|
|
||||||
<div class="form-check">
|
|
||||||
<input type="checkbox" class="form-check-input" id="forceChange" name="forceChange">
|
<input type="checkbox" class="form-check-input" id="forceChange" name="forceChange">
|
||||||
<label class="form-check-label" for="forceChange" th:text="#{adminUserSettings.forceChange}">Force user to change username/password on login</label>
|
<label class="form-check-label" for="forceChange" th:text="#{adminUserSettings.forceChange}">Force user to change username/password on login</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<button type="submit" class="btn btn-primary" th:text="#{adminUserSettings.submit}">Save User</button>
|
||||||
|
</form>
|
||||||
<div class="data-form-actions">
|
|
||||||
<button type="button" class="data-btn data-btn-secondary" data-bs-dismiss="modal">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
<span th:text="#{cancel}">Cancel</span>
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="data-btn data-btn-primary">
|
|
||||||
<span class="material-symbols-rounded">check</span>
|
|
||||||
<span th:text="#{adminUserSettings.submit}">Save User</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div class="modal-footer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Team Modal -->
|
|
||||||
<div class="modal fade" id="addTeamModal" tabindex="-1" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
|
||||||
<form th:action="@{'/api/v1/team/create'}" method="post" class="modal-content data-modal">
|
|
||||||
<div class="data-modal-header">
|
|
||||||
<h5 class="data-modal-title">
|
|
||||||
<span class="data-icon">
|
|
||||||
<span class="material-symbols-rounded">group_add</span>
|
|
||||||
</span>
|
|
||||||
<span th:text="#{adminUserSettings.createTeam}">Create Team</span>
|
|
||||||
</h5>
|
|
||||||
<button type="button" class="data-btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="data-modal-body">
|
|
||||||
<div class="data-form-group">
|
|
||||||
<label for="teamName" class="data-form-label" th:text="#{adminUserSettings.teamName}">Team Name</label>
|
|
||||||
<input type="text" name="name" id="teamName" class="data-form-control" required />
|
|
||||||
</div>
|
|
||||||
<div class="data-form-actions">
|
|
||||||
<button type="button" class="data-btn data-btn-secondary" data-bs-dismiss="modal">
|
|
||||||
<span class="material-symbols-rounded">close</span>
|
|
||||||
<span th:text="#{cancel}">Cancel</span>
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="data-btn data-btn-primary">
|
|
||||||
<span class="material-symbols-rounded">check</span>
|
|
||||||
<span th:text="#{adminUserSettings.submit}">Create</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Add User Modal end -->
|
||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
|
|
||||||
const change_confirm_text = /*[[#{adminUserSettings.confirmChangeUserStatus}]]*/ 'Should the user be disabled/enabled?';
|
|
||||||
|
|
||||||
function confirmDeleteUser() {
|
|
||||||
return confirm(delete_confirm_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmChangeUserStatus() {
|
|
||||||
return confirm(change_confirm_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery.validator.addMethod("usernamePattern", function(value, element) {
|
jQuery.validator.addMethod("usernamePattern", function(value, element) {
|
||||||
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||||
const regexUsername = /^[a-zA-Z0-9](?!.*[-@._+]{2,})([a-zA-Z0-9@._+-]{1,48})[a-zA-Z0-9]$/;
|
const regexUsername = /^[a-zA-Z0-9](?!.*[-@._+]{2,})([a-zA-Z0-9@._+-]{1,48})[a-zA-Z0-9]$/;
|
||||||
@ -381,7 +241,6 @@
|
|||||||
// Check if the field is optional or meets the requirements
|
// Check if the field is optional or meets the requirements
|
||||||
return this.optional(element) || regexUsername.test(value) || regexEmail.test(value);
|
return this.optional(element) || regexUsername.test(value) || regexEmail.test(value);
|
||||||
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
}, /*[[#{invalidUsernameMessage}]]*/ "Invalid username format");
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
@ -402,9 +261,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
username: {
|
username: {
|
||||||
usernamePattern: /*[[#{invalidUsernameMessage}]]*/ "Invalid username format"
|
usernamePattern: /*[[#{invalidUsernameMessage}]]*/ "Invalid username format"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
errorPlacement: function(error, element) {
|
errorPlacement: function(error, element) {
|
||||||
if (element.attr("name") === "username") {
|
if (element.attr("name") === "username") {
|
||||||
@ -421,40 +280,40 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#username').on('input', function() {
|
$('#username').on('input', function() {
|
||||||
var usernameInput = $(this);
|
var usernameInput = $(this);
|
||||||
var isValid = usernameInput[0].checkValidity();
|
var isValid = usernameInput[0].checkValidity();
|
||||||
var errorSpan = $('#usernameError');
|
var errorSpan = $('#usernameError');
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
usernameInput.removeClass('invalid').addClass('valid');
|
usernameInput.removeClass('invalid').addClass('valid');
|
||||||
errorSpan.hide();
|
errorSpan.hide();
|
||||||
} else {
|
} else {
|
||||||
usernameInput.removeClass('valid').addClass('invalid');
|
usernameInput.removeClass('valid').addClass('invalid');
|
||||||
errorSpan.show();
|
errorSpan.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#authType').on('change', function() {
|
$('#authType').on('change', function() {
|
||||||
var authType = $(this).val();
|
var authType = $(this).val();
|
||||||
var passwordField = $('#password');
|
var passwordField = $('#password');
|
||||||
var passwordFieldContainer = $('#passwordContainer');
|
var passwordFieldContainer = $('#passwordContainer');
|
||||||
var checkboxContainer = $('#checkboxContainer');
|
var checkboxContainer = $('#checkboxContainer');
|
||||||
|
|
||||||
if (authType === 'sso') {
|
if (authType === 'sso') {
|
||||||
passwordField.removeAttr('required');
|
passwordField.removeAttr('required');
|
||||||
passwordField.prop('disabled', true).val('');
|
passwordField.prop('disabled', true).val('');
|
||||||
passwordFieldContainer.slideUp('fast');
|
passwordFieldContainer.slideUp('fast');
|
||||||
checkboxContainer.slideUp('fast');
|
checkboxContainer.slideUp('fast');
|
||||||
} else {
|
} else {
|
||||||
passwordField.prop('disabled', false);
|
passwordField.prop('disabled', false);
|
||||||
passwordField.attr('required', 'required');
|
passwordField.attr('required', 'required');
|
||||||
passwordFieldContainer.slideDown('fast');
|
passwordFieldContainer.slideDown('fast');
|
||||||
checkboxContainer.slideDown('fast');
|
checkboxContainer.slideDown('fast');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" th:href="@{'/pdfjs-legacy/css/viewer-redact.css'}">
|
<link rel="stylesheet" th:href="@{'/pdfjs-legacy/css/viewer-redact.css'}">
|
||||||
<script th:src="@{'/pdfjs-legacy/js/viewer.mjs'}" type="module"></script>
|
<script th:src="@{'/pdfjs-legacy/js/viewer.mjs'}" type="module"></script>
|
||||||
<script src='./js/redact.js' type="module"></script>
|
<script th:src="@{'/js/redact.js'}" type="module"></script>
|
||||||
<link rel="stylesheet" th:href="@{'/css/redact.css'}">
|
<link rel="stylesheet" th:href="@{'/css/redact.css'}">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@ -695,4 +695,4 @@
|
|||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
# If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME #
|
# If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME #
|
||||||
#############################################################################################################
|
#############################################################################################################
|
||||||
|
|
||||||
|
|
||||||
security:
|
security:
|
||||||
enableLogin: false # set to 'true' to enable login
|
enableLogin: false # set to 'true' to enable login
|
||||||
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production)
|
csrfDisabled: false # set to 'true' to disable CSRF protection (not recommended for production)
|
||||||
@ -62,18 +61,32 @@ security:
|
|||||||
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
privateKey: classpath:saml-private-key.key # Your private key. Generated from your keypair
|
||||||
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
spCert: classpath:saml-public-cert.crt # Your signing certificate. Generated from your keypair
|
||||||
|
|
||||||
enterpriseEdition:
|
premium:
|
||||||
enabled: false # set to 'true' to enable enterprise edition
|
|
||||||
key: 00000000-0000-0000-0000-000000000000
|
key: 00000000-0000-0000-0000-000000000000
|
||||||
SSOAutoLogin: false # Enable to auto login to first provided SSO
|
enabled: false # Enable license key checks for pro/enterprise features
|
||||||
CustomMetadata:
|
proFeatures:
|
||||||
autoUpdateMetadata: false # set to 'true' to automatically update metadata with below values
|
SSOAutoLogin: false
|
||||||
author: username # supports text such as 'John Doe' or types such as username to autopopulate with user's username
|
CustomMetadata:
|
||||||
creator: Stirling-PDF # supports text such as 'Company-PDF'
|
autoUpdateMetadata: false # set to 'true' to automatically update metadata with below values
|
||||||
producer: Stirling-PDF # supports text such as 'Company-PDF'
|
author: username # supports text such as 'John Doe' or types such as username to autopopulate with user's username
|
||||||
|
creator: Stirling-PDF # supports text such as 'Company-PDF'
|
||||||
|
producer: Stirling-PDF # supports text such as 'Company-PDF'
|
||||||
|
googleDrive:
|
||||||
|
enabled: false
|
||||||
|
clientId: ''
|
||||||
|
apiKey: ''
|
||||||
|
appId: ''
|
||||||
|
|
||||||
|
mail:
|
||||||
|
enabled: false # set to 'true' to enable sending emails
|
||||||
|
host: smtp.example.com # SMTP server hostname
|
||||||
|
port: 587 # SMTP server port
|
||||||
|
username: '' # SMTP server username
|
||||||
|
password: '' # SMTP server password
|
||||||
|
from: '' # sender email address
|
||||||
|
|
||||||
legal:
|
legal:
|
||||||
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
termsAndConditions: https://www.stirlingpdf.com/terms # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
||||||
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
|
privacyPolicy: https://www.stirlingpdf.com/privacy-policy # URL to the privacy policy of your application (e.g. https://example.com/privacy). Empty string to disable or filename to load from local file in static folder
|
||||||
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
|
accessibilityStatement: '' # URL to the accessibility statement of your application (e.g. https://example.com/accessibility). Empty string to disable or filename to load from local file in static folder
|
||||||
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
|
cookiePolicy: '' # URL to the cookie policy of your application (e.g. https://example.com/cookie). Empty string to disable or filename to load from local file in static folder
|
||||||
@ -88,6 +101,7 @@ system:
|
|||||||
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
|
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template HTML files
|
||||||
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
tessdataDir: /usr/share/tessdata # path to the directory containing the Tessdata files. This setting is relevant for Windows systems. For Windows users, this path should be adjusted to point to the appropriate directory where the Tessdata files are stored.
|
||||||
enableAnalytics: true # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
enableAnalytics: true # set to 'true' to enable analytics, set to 'false' to disable analytics; for enterprise users, this is set to true
|
||||||
|
enableUrlToPDF: false # Set to 'true' to enable URL to PDF, INTERNAL ONLY, known security issues, should not be used externally
|
||||||
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
|
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
|
||||||
datasource:
|
datasource:
|
||||||
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
|
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
|
||||||
@ -100,13 +114,12 @@ system:
|
|||||||
name: postgres # set the name of your database. Should match the name of the database you create
|
name: postgres # set the name of your database. Should match the name of the database you create
|
||||||
customPaths:
|
customPaths:
|
||||||
pipeline:
|
pipeline:
|
||||||
watchedFoldersDir: "" #Defaults to /pipeline/watchedFolders
|
watchedFoldersDir: '' # Defaults to /pipeline/watchedFolders
|
||||||
finishedFoldersDir: "" #Defaults to /pipeline/finishedFolders
|
finishedFoldersDir: '' # Defaults to /pipeline/finishedFolders
|
||||||
operations:
|
operations:
|
||||||
weasyprint: "" #Defaults to /opt/venv/bin/weasyprint
|
weasyprint: '' # Defaults to /opt/venv/bin/weasyprint
|
||||||
unoconvert: "" #Defaults to /opt/venv/bin/unoconvert
|
unoconvert: '' # Defaults to /opt/venv/bin/unoconvert
|
||||||
|
fileUploadLimit: '' # Defaults to "". No limit when string is empty. Set a number, between 0 and 999, followed by one of the following strings to set a limit. "KB", "MB", "GB".
|
||||||
|
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
appName: '' # application's visible name
|
appName: '' # application's visible name
|
||||||
|
Loading…
Reference in New Issue
Block a user