mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into maintenance/update-translations
This commit is contained in:
commit
bfdf31cbb5
@ -18,6 +18,7 @@
|
||||
- Fix bug where a reaction loop could be created, but would be cut short by an error when the loop occurred ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/636))
|
||||
- Use controller on update user call ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/640))
|
||||
- Move `imports` to `import/upload` in bind mount to better serve users on unraid with limited vdisk storage ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/642))
|
||||
- Fix bug where everytime a page was loaded user settings would be reset #286 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/643))
|
||||
### Refactor
|
||||
- Consolidate remaining frontend functions into API V2, and remove ajax internal API ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/585))
|
||||
- Replace bleach with nh3 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/628))
|
||||
@ -33,8 +34,10 @@
|
||||
- Bump orjson to 3.9.7 for python 3.12 support ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/638))
|
||||
- Bump all Crafty required python dependancies, maintaining minimum 3.9 support ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/639))
|
||||
- Better optimize and refactor docker launcher sh ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/642))
|
||||
- Improve pop-up notifications with Toasts ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/641))
|
||||
- Move username and password settings to buttons on panel config ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/643))
|
||||
### Lang
|
||||
TBD
|
||||
- fr_FR Translation Updated to latest en_EN ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/646))
|
||||
<br><br>
|
||||
|
||||
## --- [4.1.3] - 2023/07/18
|
||||
|
@ -214,14 +214,14 @@ class UsersController:
|
||||
limit_server_creation = 0
|
||||
limit_user_creation = 0
|
||||
limit_role_creation = 0
|
||||
|
||||
PermissionsCrafty.add_or_update_user(
|
||||
user_id,
|
||||
permissions_mask,
|
||||
limit_server_creation,
|
||||
limit_user_creation,
|
||||
limit_role_creation,
|
||||
)
|
||||
if user_crafty_data:
|
||||
PermissionsCrafty.add_or_update_user(
|
||||
user_id,
|
||||
permissions_mask,
|
||||
limit_server_creation,
|
||||
limit_user_creation,
|
||||
limit_role_creation,
|
||||
)
|
||||
|
||||
self.users_helper.delete_user_roles(user_id, removed_roles)
|
||||
|
||||
|
@ -4,10 +4,7 @@ import typing as t
|
||||
|
||||
from jsonschema import ValidationError, validate
|
||||
from app.classes.controllers.users_controller import UsersController
|
||||
from app.classes.models.crafty_permissions import (
|
||||
EnumPermissionsCrafty,
|
||||
PermissionsCrafty,
|
||||
)
|
||||
from app.classes.models.crafty_permissions import EnumPermissionsCrafty
|
||||
from app.classes.models.roles import HelperRoles
|
||||
from app.classes.models.users import HelperUsers
|
||||
from app.classes.web.base_api_handler import BaseApiHandler
|
||||
@ -247,31 +244,25 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
or data["manager"] == 0
|
||||
):
|
||||
data["manager"] = None
|
||||
|
||||
crafty_perms = None
|
||||
if "permissions" in data:
|
||||
permissions: t.List[UsersController.ApiPermissionDict] = data.pop(
|
||||
"permissions"
|
||||
)
|
||||
permissions_mask = "0" * len(EnumPermissionsCrafty)
|
||||
limit_server_creation = 0
|
||||
limit_user_creation = 0
|
||||
limit_role_creation = 0
|
||||
|
||||
for permission in permissions:
|
||||
permissions_mask = self.controller.crafty_perms.set_permission(
|
||||
permissions_mask,
|
||||
EnumPermissionsCrafty.__members__[permission["name"]],
|
||||
"1" if permission["enabled"] else "0",
|
||||
)
|
||||
|
||||
PermissionsCrafty.add_or_update_user(
|
||||
user_id,
|
||||
permissions_mask,
|
||||
limit_server_creation,
|
||||
limit_user_creation,
|
||||
limit_role_creation,
|
||||
)
|
||||
|
||||
if permissions is not None:
|
||||
server_quantity = {}
|
||||
permissions_mask = list(permissions_mask)
|
||||
for permission in permissions:
|
||||
server_quantity[permission["name"]] = permission["quantity"]
|
||||
permissions_mask[
|
||||
EnumPermissionsCrafty[permission["name"]].value
|
||||
] = ("1" if permission["enabled"] else "0")
|
||||
permissions_mask = "".join(permissions_mask)
|
||||
crafty_perms = {
|
||||
"permissions_mask": permissions_mask,
|
||||
"server_quantity": server_quantity,
|
||||
}
|
||||
# TODO: make this more efficient
|
||||
if len(data) != 0:
|
||||
for key in data:
|
||||
@ -280,7 +271,11 @@ class ApiUsersUserIndexHandler(BaseApiHandler):
|
||||
if key == "password":
|
||||
value = self.helper.encode_pass(value)
|
||||
setattr(user_obj, key, value)
|
||||
self.controller.users.update_user(auth_data[4]["user_id"], data)
|
||||
self.controller.users.update_user(
|
||||
user_id,
|
||||
data,
|
||||
crafty_perms,
|
||||
)
|
||||
|
||||
self.controller.management.add_to_audit_log(
|
||||
user["user_id"],
|
||||
|
@ -124,19 +124,32 @@
|
||||
|
||||
.notification {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 0.5rem;
|
||||
padding-left: 0.7rem;
|
||||
width: 180px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
margin-right: 1rem;
|
||||
background: var(--card-banner-bg);
|
||||
-webkit-transition: right 0.75s, opacity 0.75s, top 0.75s;
|
||||
-moz-transition: right 0.75s, opacity 0.75s, top 0.75s;
|
||||
-o-transition: right 0.75s, opacity 0.75s, top 0.75s;
|
||||
transition: right 0.75s, opacity 0.75s, top 0.75s;
|
||||
right: -6rem;
|
||||
right: -20rem;
|
||||
opacity: 0.1;
|
||||
margin-bottom: 1rem;
|
||||
z-index: 999;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.toast-header {
|
||||
background-color: var(--card-banner-bg);
|
||||
color: var(--base-text);
|
||||
}
|
||||
|
||||
.toast-body {
|
||||
background-color: var(--dropdown-bg);
|
||||
color: var(--base-text);
|
||||
}
|
||||
|
||||
.notification img {
|
||||
max-height: 20px;
|
||||
}
|
||||
|
||||
.notification strong {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.notification.active {
|
||||
@ -150,27 +163,17 @@
|
||||
top: -2rem;
|
||||
}
|
||||
|
||||
.notification p {
|
||||
margin: 0px;
|
||||
width: calc(160.8px - 16px);
|
||||
z-index: inherit;
|
||||
}
|
||||
|
||||
.notification span {
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 0.46rem;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
font-size: 22px;
|
||||
font-size: 15px;
|
||||
user-select: none;
|
||||
z-index: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<div class="notifications"></div>
|
||||
|
||||
|
||||
<div class="notifications"></div>
|
||||
|
||||
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
|
||||
<script src="/static/assets/js/shared/off-canvas.js"></script>
|
||||
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
|
||||
@ -502,30 +505,55 @@
|
||||
|
||||
function notify(message) {
|
||||
console.log(`notify(${message})`);
|
||||
var paragraphEl = document.createElement('p');
|
||||
var closeEl = document.createElement('span');
|
||||
var intId = getRandomInt(0, 100);
|
||||
var toastDiv = document.createElement('div');
|
||||
toastDiv.setAttribute("id", "toast_" + intId);
|
||||
toastDiv.setAttribute("class", "notification toast");
|
||||
toastDiv.setAttribute("role", "alert");
|
||||
toastDiv.setAttribute("aria-lived", "assertive");
|
||||
toastDiv.setAttribute("aria-atomic", "true");
|
||||
toastDiv.setAttribute("data-delay", "3000");
|
||||
toastDiv.setAttribute("data-animation", "true");
|
||||
toastDiv.setAttribute("data-autohide", "false");
|
||||
|
||||
paragraphEl.textContent = message;
|
||||
var toastHeaderDiv = document.createElement('div');
|
||||
toastHeaderDiv.setAttribute("class", "toast-header");
|
||||
|
||||
closeEl.innerHTML = '×';
|
||||
closeEl.addEventListener('click', function () { closeNotification(this) });
|
||||
var toastHeaderImg = document.createElement('img');
|
||||
toastHeaderImg.setAttribute("src", "/static/assets/images/logo_small.svg");
|
||||
toastHeaderImg.setAttribute("class", "mr-auto");
|
||||
toastHeaderImg.setAttribute("alt", "logo");
|
||||
toastHeaderDiv.appendChild(toastHeaderImg);
|
||||
|
||||
var parentEl = document.createElement('div');
|
||||
parentEl.appendChild(paragraphEl);
|
||||
parentEl.appendChild(closeEl);
|
||||
var toastHeaderTitle = document.createElement('strong');
|
||||
toastHeaderTitle.setAttribute("class", "mr-auto");
|
||||
toastHeaderTitle.innerHTML = " Crafty Controller ";
|
||||
toastHeaderDiv.appendChild(toastHeaderTitle);
|
||||
|
||||
parentEl.classList.add('notification');
|
||||
var toastHeaderCloseSpan = document.createElement('span');
|
||||
toastHeaderCloseSpan.setAttribute("class", "fa-solid fa-xmark");
|
||||
toastHeaderCloseSpan.setAttribute("aria-hidden", "true");
|
||||
toastHeaderCloseSpan.addEventListener('click', function () { closeNotification(this.parentElement) });
|
||||
toastHeaderDiv.appendChild(toastHeaderCloseSpan);
|
||||
|
||||
document.querySelector('.notifications').appendChild(parentEl);
|
||||
var toastBodyDiv = document.createElement('div');
|
||||
toastBodyDiv.setAttribute("class", "toast-body");
|
||||
toastBodyDiv.textContent = message;
|
||||
|
||||
toastDiv.appendChild(toastHeaderDiv);
|
||||
toastDiv.appendChild(toastBodyDiv);
|
||||
|
||||
document.querySelector('.notifications').appendChild(toastDiv);
|
||||
|
||||
$('#toast_' + intId).toast('show');
|
||||
|
||||
setTimeout(function () {
|
||||
parentEl.classList.add('active');
|
||||
toastDiv.classList.add('active');
|
||||
}, 200);
|
||||
|
||||
setTimeout(function (element) {
|
||||
closeNotification(element);
|
||||
}, 7500, closeEl);
|
||||
closeNotification(element.parentElement);
|
||||
}, 7500, toastHeaderCloseSpan);
|
||||
|
||||
`
|
||||
<div class="notification">
|
||||
|
@ -992,23 +992,28 @@
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
function sendOrder(id_string) {
|
||||
async function sendOrder(id_string) {
|
||||
const token = getCookie("_xsrf")
|
||||
|
||||
$.ajax({
|
||||
type: "PATCH",
|
||||
headers: { 'X-XSRFToken': token },
|
||||
url: `/api/v2/users/@me`,
|
||||
data: JSON.stringify({
|
||||
let res = await fetch(`/api/v2/users/@me`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-XSRFToken': token
|
||||
},
|
||||
body: JSON.stringify({
|
||||
server_order: id_string,
|
||||
}),
|
||||
success: function (data) {
|
||||
console.log("got response:");
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
let responseData = await res.json();
|
||||
if (responseData.status === "ok") {
|
||||
return
|
||||
} else {
|
||||
|
||||
bootbox.alert({
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
// Inits the sortable
|
||||
$("table#servers_table tbody")
|
||||
.sortable({
|
||||
|
@ -79,7 +79,7 @@
|
||||
<tbody>
|
||||
{% for user in data['users'] %}
|
||||
<tr>
|
||||
<td><i class="fas fa-user"></i> {{ user.username }}</td>
|
||||
<td><i class="fas fa-user"></i><span id="user_{{user.user_id}}">{{ user.username }}</span></td>
|
||||
<td>
|
||||
{% if user.enabled %}
|
||||
<span class="text-success">
|
||||
@ -106,7 +106,10 @@
|
||||
{% end %}
|
||||
</ul>
|
||||
</td>
|
||||
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||
<td><span data-translate="{{translate('userConfig', 'userName', data['lang'])}}" data-toggle="tooltip" title="{{ translate('userConfig', 'userName', data['lang'])}}" id="username_{{user.user_id}}" class="edit_user clickable" data-name="{{user.username}}" data-id="{{user.user_id}}"><i class="fa-solid fa-user"></i></span>
|
||||
<span data-translate1="{{translate('userConfig', 'password', data['lang'])}}" data-translate2="{{translate('userConfig', 'repeat', data['lang'])}}" data-toggle="tooltip" title="{{ translate('userConfig', 'password', data['lang'])}}" class="edit_password clickable" data-id="{{user.user_id}}"><i class="fa-solid fa-lock"></i></span>
|
||||
<a data-toggle="tooltip" title="{{ translate('userConfig', 'pageTitle', data['lang'])}}" href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
{% for user in data['managed_users'] %}
|
||||
@ -138,7 +141,10 @@
|
||||
{% end %}
|
||||
</ul>
|
||||
</td>
|
||||
<td><a href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a></td>
|
||||
<td><span data-translate="{{translate('userConfig', 'userName', data['lang'])}}" data-toggle="tooltip" title="{{ translate('userConfig', 'userName', data['lang'])}}" id="username_{{user.user_id}}" class="edit_user clickable" data-name="{{user.username}}" data-id="{{user.user_id}}"><i class="fa-solid fa-user"></i></span>
|
||||
<span data-translate1="{{translate('userConfig', 'password', data['lang'])}}" data-translate2="{{translate('userConfig', 'repeat', data['lang'])}}" data-toggle="tooltip" title="{{ translate('userConfig', 'password', data['lang'])}}" class="edit_password clickable" data-id="{{user.user_id}}"><i class="fa-solid fa-lock"></i></span>
|
||||
<a data-toggle="tooltip" title="{{ translate('userConfig', 'pageTitle', data['lang'])}}" href="/panel/edit_user?id={{user.user_id}}"><i class="fas fa-pencil-alt"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
@ -274,6 +280,12 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.clickable {
|
||||
color: #007bff;
|
||||
}
|
||||
.clickable:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.custom-picker {
|
||||
border: 1px solid var(--outline);
|
||||
}
|
||||
@ -312,6 +324,99 @@
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
function validateForm() {
|
||||
let password0 = document.getElementById("password0").value;
|
||||
let password1 = document.getElementById("password1").value;
|
||||
if (password0 != password1) {
|
||||
$('.passwords-match').popover('show');
|
||||
$('.popover-body').click(function () {
|
||||
$('.passwords-match').popover("hide");
|
||||
});
|
||||
document.body.scrollTop = 0;
|
||||
document.documentElement.scrollTop = 0;
|
||||
$("#password0").css("outline", "1px solid red");
|
||||
$("#password1").css("outline", "1px solid red");
|
||||
return false;
|
||||
} else {
|
||||
return password1;
|
||||
}
|
||||
}
|
||||
$(".edit_password").on("click", async function(){
|
||||
const token = getCookie("_xsrf");
|
||||
let user_id = $(this).data('id');
|
||||
bootbox.confirm(`<form class="form" id='infos' action=''>\
|
||||
<div class="form-group">
|
||||
<label for="new_password">${$(this).data("translate1")}</label>
|
||||
<input class="form-control" type='password' id="password0" name='new_password' /></br>\
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">${$(this).data("translate2")}</label>
|
||||
<input class="form-control" type='password' id="password1" name='confirm_password' />\
|
||||
</div>
|
||||
</form>`, async function(result) {
|
||||
if(result){
|
||||
password = validateForm();
|
||||
if (!password){
|
||||
return;
|
||||
}
|
||||
let res = await fetch(`/api/v2/users/${user_id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-XSRFToken': token
|
||||
},
|
||||
body: JSON.stringify({"password": password}),
|
||||
});
|
||||
let responseData = await res.json();
|
||||
if (responseData.status === "ok") {
|
||||
console.log(responseData.data)
|
||||
} else {
|
||||
|
||||
bootbox.alert({
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
$(document).on("submit", ".bootbox form", function(e) {
|
||||
e.preventDefault();
|
||||
$(".bootbox .btn-primary").click();
|
||||
});
|
||||
|
||||
$(".edit_user").on("click", function(){
|
||||
const token = getCookie("_xsrf");
|
||||
let username = $(this).data('name');
|
||||
let user_id = $(this).data('id');
|
||||
bootbox.confirm(`<form class="form" id='infos' action=''>\
|
||||
<div class="form-group">
|
||||
<label for="username">${$(this).data("translate")}</label>
|
||||
<input class="form-control" type='text' name='username' id="username_field" value=${username} /><br/>\
|
||||
</div>
|
||||
</form>`, async function(result) {
|
||||
if(result){
|
||||
let new_username = $("#username_field").val();
|
||||
let res = await fetch(`/api/v2/users/${user_id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-XSRFToken': token
|
||||
},
|
||||
body: JSON.stringify({"username": new_username}),
|
||||
});
|
||||
let responseData = await res.json();
|
||||
if (responseData.status === "ok") {
|
||||
$(`#user_${user_id}`).html(` ${new_username}`)
|
||||
$(`#username_${user_id}`).data('name', new_username);
|
||||
} else {
|
||||
|
||||
bootbox.alert({
|
||||
title: responseData.status,
|
||||
message: responseData.error
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (webSocket) {
|
||||
webSocket.on('move_status', function (message) {
|
||||
if (message === "done") {
|
||||
|
@ -71,6 +71,7 @@ data['lang']) }}{% end %}
|
||||
data['lang']) }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if data['new_user'] %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang'])
|
||||
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang'])
|
||||
@ -98,6 +99,15 @@ data['lang']) }}{% end %}
|
||||
data-content="{{ translate('panelConfig', 'match', data['lang']) }}" ,
|
||||
data-placement="right"></span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">{{ translate('userConfig', 'userName', data['lang'])
|
||||
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'userNameDesc', data['lang'])
|
||||
}}</small> </label>
|
||||
<input type="text" class="form-control" name="username" id="username" autocomplete="off"
|
||||
data-lpignore="true" value="{{ data['user']['username'] }}" placeholder="User Name" disabled>
|
||||
</div>
|
||||
{% end %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">{{ translate('userConfig', 'gravEmail', data['lang'])
|
||||
}}<small class="text-muted ml-1"> - {{ translate('userConfig', 'gravDesc', data['lang'])
|
||||
@ -388,18 +398,43 @@ data['lang']) }}{% end %}
|
||||
return (isNaN(value) ? value : +value);
|
||||
}
|
||||
}
|
||||
const userId = new URLSearchParams(document.location.search).get('id')
|
||||
$("#user_form").on("submit", async function (e) {
|
||||
const userId = new URLSearchParams(document.location.search).get('id');
|
||||
console.log(userId)
|
||||
e.preventDefault();
|
||||
let password = validateForm();
|
||||
if (!password){
|
||||
return;
|
||||
let password = null;
|
||||
if(!userId){
|
||||
password = validateForm();
|
||||
if (!password){
|
||||
return;
|
||||
}
|
||||
}
|
||||
const token = getCookie("_xsrf")
|
||||
|
||||
let userRes = await fetch(`/api/v2/users/@me`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
'X-XSRFToken': token
|
||||
},
|
||||
});
|
||||
let userData = await userRes.json();
|
||||
let superuser = null;
|
||||
if (userData.status === "ok") {
|
||||
superuser = userData.data["superuser"];
|
||||
edit_id = userData.data["user_id"];
|
||||
} else {
|
||||
bootbox.alert({
|
||||
title: userData.error,
|
||||
message: userData.error
|
||||
});
|
||||
}
|
||||
|
||||
let userForm = document.getElementById("user_form");
|
||||
|
||||
let disabled_flag = false;
|
||||
let roles = $('.role_check').map(function() {
|
||||
let roles = null;
|
||||
if (superuser || userId != edit_id){
|
||||
roles = $('.role_check').map(function() {
|
||||
if ($(this).attr("disabled")){
|
||||
disabled_flag = true;
|
||||
}
|
||||
@ -407,7 +442,6 @@ data['lang']) }}{% end %}
|
||||
return $(this).val();
|
||||
}
|
||||
}).get();
|
||||
|
||||
let avail_permissions = $('.perm-name').map(function() {
|
||||
return $(this).data("perm");
|
||||
}).get();
|
||||
@ -416,20 +450,26 @@ data['lang']) }}{% end %}
|
||||
for(i=0; i < avail_permissions.length; i++){
|
||||
permissions.push({"name": avail_permissions[i], "quantity": $(`#quantity_${avail_permissions[i]}`).val(), "enabled": $(`#permission_${avail_permissions[i]}`).is(':checked')})
|
||||
}
|
||||
console.log(permissions);
|
||||
|
||||
}
|
||||
let formData = new FormData(userForm);
|
||||
//Create an object from the form data entries
|
||||
let formDataObject = Object.fromEntries(formData.entries());
|
||||
if(userId){
|
||||
delete formDataObject.username
|
||||
}
|
||||
if (superuser || userId != edit_id){
|
||||
if (!disabled_flag){
|
||||
formDataObject.roles = roles;
|
||||
}
|
||||
if ($("#permissions").length){
|
||||
formDataObject.permissions = permissions;
|
||||
}
|
||||
if(typeof password === "string"){
|
||||
if(!userId){
|
||||
if(typeof password === "string"){
|
||||
formDataObject.password = password;
|
||||
}
|
||||
}
|
||||
}
|
||||
formDataObject.enabled = $("#enabled").is(":checked");
|
||||
if ($("#superuser").is(":enabled")){
|
||||
formDataObject.superuser = $("#superuser").is(":checked");
|
||||
|
@ -615,7 +615,7 @@
|
||||
"pageTitleNew": "Create User",
|
||||
"password": "New Password",
|
||||
"permName": "Permission Name",
|
||||
"repeat": "Repeat Password",
|
||||
"repeat": "Confirm Password",
|
||||
"roleName": "Role Name",
|
||||
"selectManager": "Select Manager for User",
|
||||
"super": "Super User",
|
||||
|
@ -53,6 +53,20 @@
|
||||
"translationTitle": "Traductions",
|
||||
"translator": "Traducteurs"
|
||||
},
|
||||
"customLogin": {
|
||||
"apply": "Appliquer",
|
||||
"backgroundUpload": "Charger l'arrière plan",
|
||||
"customLoginPage": "Personnaliser la page de Connexion",
|
||||
"delete": "Supprimer",
|
||||
"labelLoginImage": "Choisis l'arrière plan de Connexion",
|
||||
"loginBackground": "Image d'arrière plan de Login",
|
||||
"loginImage": "Charger une image de fond pour l'écran de Connexion.",
|
||||
"loginOpacity": "Selectionner l'opacité de la fenêtre de Connexion",
|
||||
"pageTitle": "Page Personalisée de Connexion",
|
||||
"preview": "Aperçu",
|
||||
"select": "Sélectionner",
|
||||
"selectImage": "Sélectionner une image"
|
||||
},
|
||||
"dashboard": {
|
||||
"actions": "Actions",
|
||||
"allServers": "Tous les Serverus",
|
||||
@ -165,20 +179,33 @@
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"agree": "Agree",
|
||||
"bedrockError": "Téléchargement Bedrock non disponible. Merci de vérifier",
|
||||
"cancel": "Annuler",
|
||||
"contact": "Contacter le Support de Crafty Control via Discord",
|
||||
"craftyStatus": "Page de statut de Crafty",
|
||||
"cronFormat": "Format Cron invalide détecté",
|
||||
"embarassing": "Oulà, c'est embarrassant.",
|
||||
"error": "Erreur !",
|
||||
"eulaAgree": "Êtes-vous d'accord?",
|
||||
"eulaMsg": "Vous devez accepter le EULA. Une copie du CLUF de Minecraft est liée sous ce message.",
|
||||
"eulaTitle": "Accepter le EULA",
|
||||
"fileError": "Le type de fichier doit être une image.",
|
||||
"fileTooLarge": "Echec du chargement. Le fichier est trop gros. Demande de l'aide à l'administrateur système.",
|
||||
"hereIsTheError": "Il y a une erreur",
|
||||
"installerJava": "Echec de l'installation {} : l'installation d'un server Forge nécessite Java. Nous avons détecté que Java n'est pas installé. Merci d'installer Java, puis le serveur.",
|
||||
"internet": "Nous avons détecté que la machine exécutant Crafty n'a pas de connexion à Internet. Les connexions client au serveur peuvent être limitées.",
|
||||
"migration": "Le stockage principal de serverurs de Crafty est en migration vers la nouvelle localisation. Tous les serveurs ont été suspendu pour l'opération. Merci de patienter pendant que nous finissons la migration.",
|
||||
"no-file": "Nous ne parvenons pas à localiser le fichier demandé. Vérifier le chemin du fichier. Les permissions authorisent elles Crafty ?",
|
||||
"noInternet": "Crafty a des diggiculté à accéder à Internet. La création de serveur a été désactivée. Merci de vérifier ta connexion internet et de rafraichir cette page.",
|
||||
"noJava": "Le Démarrage du Serveur {} a échoué avec le code d'erreur : Nous avons détecté que Java n'est pas installé. Merci d'installer java avant de démarrer le serveur.",
|
||||
"not-downloaded": "Nous ne parvenons pas à trouver le fichier exécutable. A-t-il fini de Télécharger ? Les permissions permettent elles l'exécution ?",
|
||||
"portReminder": "Nous avons détecté que c'est la première fois que {} est exécuté. Assurez-vous de transférer le port {} via votre routeur/pare-feu pour le rendre accessible à distance depuis Internet.",
|
||||
"privMsg": "et le ",
|
||||
"serverJars1": "l'API Server JARs est inaccessible. Merci de vérifier",
|
||||
"serverJars2": "pour les informations les plus à jour.",
|
||||
"start-error": "Le serveur {} n'a pas pu démarrer avec le code d'erreur : {}",
|
||||
"superError": "Tu dois être un super utilisateur pour effectuer cette action.",
|
||||
"terribleFailure": "C'est une Terrible Erreur !"
|
||||
},
|
||||
"footer": {
|
||||
@ -190,7 +217,8 @@
|
||||
"forgotPassword": "Mot de Passe Oublié",
|
||||
"login": "Connexion",
|
||||
"password": "Mot de Passe",
|
||||
"username": "Nom d'Utilisateur"
|
||||
"username": "Nom d'Utilisateur",
|
||||
"viewStatus": "Voir la page de statut publique"
|
||||
},
|
||||
"notify": {
|
||||
"activityLog": "Logs d'Activité",
|
||||
@ -202,24 +230,38 @@
|
||||
"preparingLogs": " Merci d'attendre pendant que nous préparons les logs ... Nous enverrons une notification quand ils seront prêts. Cela peut prendre du temps s'il y a beaucoup de serveurs.",
|
||||
"supportLogs": "Logs de Support"
|
||||
},
|
||||
"offline": {
|
||||
"offline": "Hors ligne",
|
||||
"pleaseConnect": "Merci de se connecter à Internet pour utiliser Crafty."
|
||||
},
|
||||
"panelConfig": {
|
||||
"adminControls": "Controls de l'Admin",
|
||||
"allowedServers": "Serveurs Authorisés",
|
||||
"apply": "Appliquer",
|
||||
"assignedRoles": "Rôles Assignés",
|
||||
"cancel": "Annuler",
|
||||
"clearComms": "Nettoyer les Commandes Non-Exécutées",
|
||||
"custom": "Personnaliser Crafty",
|
||||
"delete": "Supprimer",
|
||||
"edit": "Modifier",
|
||||
"enableLang": "Activer toutes les langues",
|
||||
"enabled": "Activé",
|
||||
"globalExplain": "L'endroit où Crafty stocke tous les fichiers des serveurs. (Nous suffixer le chemin avec /servers/[uuid of server])",
|
||||
"globalServer": "Dossier Global des Serveurs",
|
||||
"json": "Config.json",
|
||||
"match": "Les mots de passe doivent correspondre",
|
||||
"newRole": "Ajouter un nouveau Rôle",
|
||||
"newUser": "Ajouter un Nouvel Utilisateur",
|
||||
"noMounts": "Ne montrer aucun point de montage sur le tableau de bord",
|
||||
"pageTitle": "Panneau de Configuration",
|
||||
"role": "Rôle",
|
||||
"roleUsers": "Rôles Utilisteurs",
|
||||
"roles": "Roles",
|
||||
"save": "Enregister",
|
||||
"select": "Sélectionner",
|
||||
"superConfirm": "Utiliser seulement si tu veux que cet utilisateur ait accès à ABSOLUMENT TOUT (tous les comptes, tous les serveurs, panneau de configuration, etc). Ils peuvent même supprimer tes droits superuser.",
|
||||
"superConfirmTitle": "Activer le superuser ? Es-tu sûr ?",
|
||||
"title": "Configuration de Crafty",
|
||||
"user": "Utilisateur",
|
||||
"users": "Utilisateurs"
|
||||
},
|
||||
@ -243,14 +285,17 @@
|
||||
"roleTitle": "Paramètres du Rôle",
|
||||
"roleUserName": "Nom d'Utilisateur",
|
||||
"roleUsers": "Les utilisateurs du Rôle: ",
|
||||
"selectManager": "Sélectionne un gestionnaire pour ce role",
|
||||
"serverAccess": "Accès ?",
|
||||
"serverName": "Nom du Serveur",
|
||||
"serversDesc": "Les serveurs auquels ce rôle a accès"
|
||||
},
|
||||
"serverBackups": {
|
||||
"after": "Exécuter une commande après la sauvegarde",
|
||||
"backupAtMidnight": "Sauvegarde Automatique à minuit ?",
|
||||
"backupNow": "Sauvegarder Maintenant !",
|
||||
"backupTask": "Une sauvegarde vient de démarrer.",
|
||||
"before": "Exécuter une commande avant la sauvegarde",
|
||||
"cancel": "Annuler",
|
||||
"clickExclude": "Cliquer pour sélectionner les Exclusions",
|
||||
"compress": "Compresser la Sauvegarde",
|
||||
@ -281,7 +326,7 @@
|
||||
"bePatientDeleteFiles": "Merci de patienter pendant la suppression du serveur du tableau de bord de Crafty et des fichiers de la machine hôte. Cet écran se fermera dans quelques instants.",
|
||||
"bePatientUpdate": "Merci de patienter pendant la mise à jour du Serveur. La durée de téléchargement dépend de votre vitesse de connexion internet.<br /> Cet écran se mettra à jour dans quelques instants",
|
||||
"cancel": "Annuler",
|
||||
"crashTime": "Crash Timeout",
|
||||
"crashTime": "Délai de plantage",
|
||||
"crashTimeDesc": "Combien de temps attendre avant de considérer que le serveur a crash ?",
|
||||
"deleteFilesQuestion": "Supprimer les fichiers de la machine ?",
|
||||
"deleteFilesQuestionMessage": "Veux-tu que Crafty supprime tous les fichiers du serveur de la machine hôte? <br><br><strong>Cela inclut les sauvegardes du serveur.</strong>",
|
||||
@ -290,6 +335,8 @@
|
||||
"deleteServerQuestionMessage": "Es-tu sur de vouloir supprimer ce Serveur ? Après ça, il n'y aura pas de retour en arrière ...",
|
||||
"exeUpdateURL": "Lien URL de mise à jour de l'exécutable du Serveur",
|
||||
"exeUpdateURLDesc": "Lien URL de Téléchargement Direct pour les mises à jour.",
|
||||
"ignoredExits": "Codes d'Erreurs de Plantage Ignorés",
|
||||
"ignoredExitsExplain": "Codes d'Erreurs de sortie que Crafty doit considérer comme un arrêt nominal (séparés par une virgule)",
|
||||
"javaNoChange": "Ne Pas Remplacer",
|
||||
"javaVersion": "Remplace la version actuelle de Java",
|
||||
"javaVersionDesc": "Si tu veux remplacer la version de Java, assure toi que le chemin vers la version de Java dans 'execution command' est bien entre guillements (par défaut sans la variable 'java')",
|
||||
@ -303,7 +350,7 @@
|
||||
"serverAutoStart": "Autodémarrage du Serveur",
|
||||
"serverAutostartDelay": "Délai d'Autodémarrage du Serveur",
|
||||
"serverAutostartDelayDesc": "Délai avant autodémarrage (Si activé ci-dessous)",
|
||||
"serverCrashDetection": "Détaction de Plantage du Serveur",
|
||||
"serverCrashDetection": "Détection de Plantage du Serveur",
|
||||
"serverExecutable": "Exécutable du Serveur",
|
||||
"serverExecutableDesc": "Le fichier exécutable du serveur",
|
||||
"serverExecutionCommand": "Commande d'Exécution du Serveur",
|
||||
@ -320,7 +367,13 @@
|
||||
"serverPortDesc": "Port auquel Crafty doit se connecter pour les stats",
|
||||
"serverStopCommand": "Commande d'Arrêt du Serveur",
|
||||
"serverStopCommandDesc": "Commande à envoyer pour l'arrêter",
|
||||
"showStatus": "Montrer sur la page de statut publique",
|
||||
"shutdownTimeout": "Délai d'arret",
|
||||
"statsHint1": "Le port sur lequel le serveur tourne doit aller ici. Ce n'est que pour que Crafty puisse se connecter pour récupérer les statistiques du serveur.",
|
||||
"statsHint2": "Cela ne change pas le port de ton serveur. Tu doit quand même changer le port dans le fichier de configuration du serveur.",
|
||||
"stopBeforeDeleting": "Merci d'arrêter le serveur avant de le supprimer",
|
||||
"timeoutExplain1": "La durée pendant laquelle Crafty va attendre l'arret du server après exécution de la",
|
||||
"timeoutExplain2": "commande avant de forcer la fin du processus.",
|
||||
"update": "Mettre à Jour l'Exécutable",
|
||||
"yesDelete": "Oui, Supprimer",
|
||||
"yesDeleteFiles": "Oui, Supprimer les fichier"
|
||||
@ -346,8 +399,12 @@
|
||||
"backup": "Sauvegardes",
|
||||
"config": "Configuration",
|
||||
"files": "Fichiers",
|
||||
"filter": "Filtrer les Logs",
|
||||
"filterList": "Mots Filtrés",
|
||||
"logs": "Logs",
|
||||
"metrics": "Statistiques",
|
||||
"playerControls": "Gestion des Joueurs",
|
||||
"reset": "Réinitialiser le défilement",
|
||||
"schedule": "Tâches Planifiées",
|
||||
"serverDetails": "Détails Serveur",
|
||||
"terminal": "Terminal"
|
||||
@ -416,18 +473,36 @@
|
||||
"parent-explain": "Quelle tâche doit déclencher celle-ci ?",
|
||||
"reaction": "Réaction",
|
||||
"restart": "Redémarrer le Serveur",
|
||||
"select": "Sélection basique de la chaine de réaction / Cron /",
|
||||
"start": "Démarrer le Serveur",
|
||||
"stop": "Arrêter le Server",
|
||||
"time": "Temps",
|
||||
"time-explain": "A quelle heure exécuter la tâche planifiée ?"
|
||||
},
|
||||
"serverSchedules": {
|
||||
"action": "Action",
|
||||
"areYouSure": "Supprimer la Tâche Planifiée ?",
|
||||
"cancel": "Annuler",
|
||||
"cannotSee": "Tu ne peux pas tou voir ?",
|
||||
"cannotSee": "Tu ne peux pas tout voir ?",
|
||||
"cannotSeeOnMobile": "Essaie de clicker sur une tâche planifiée pour voir tous les détails.",
|
||||
"child": "Enfant de la tâche planifiée avec l'ID ",
|
||||
"close": "Fermer",
|
||||
"command": "Commande",
|
||||
"confirm": "Confirmer",
|
||||
"confirmDelete": "Es-tu sûr de vouloir supprimer cette tâche planifiée ? Il n'y aura pas de retour en arrière."
|
||||
"confirmDelete": "Es-tu sûr de vouloir supprimer cette tâche planifiée ? Il n'y aura pas de retour en arrière.",
|
||||
"create": "Créer une nouvelle tâche planifiée",
|
||||
"cron": "Crong String",
|
||||
"delete": "Supprimer",
|
||||
"details": "Détails de la tâche planifiée",
|
||||
"edit": "Modifier",
|
||||
"enabled": "Activer",
|
||||
"every": "Toutes",
|
||||
"interval": "Intervalle",
|
||||
"name": "Nom",
|
||||
"nextRun": "Exécution suivante",
|
||||
"no": "Non",
|
||||
"scheduledTasks": "Tâches Planifiées",
|
||||
"yes": "Oui"
|
||||
},
|
||||
"serverStats": {
|
||||
"cpuUsage": "Utilisation CPU",
|
||||
@ -450,6 +525,7 @@
|
||||
"commandInput": "Entre ta commande",
|
||||
"delay-explained": "Le service/agent a récemment démarré et retarde le démarrage de l'instance du serveur minecraft",
|
||||
"downloading": "Téléchargement ...",
|
||||
"importing": "Importation ...",
|
||||
"installing": "Installation ...",
|
||||
"restart": "Redémarrer",
|
||||
"sendCommand": "Envoiyer commande",
|
||||
@ -475,6 +551,7 @@
|
||||
"importServerButton": "Importer Serveur !",
|
||||
"importZip": "Importer depuis un Fichier Zip",
|
||||
"importing": "Importation du Serveur ...",
|
||||
"labelZipFile": "Choisis ton Fichier Zip",
|
||||
"maxMem": "Mémoire Maximum",
|
||||
"minMem": "Mémoire Minimum",
|
||||
"myNewServer": "Mon Nouveau Serveur",
|
||||
@ -485,6 +562,7 @@
|
||||
"save": "Sauvegarder",
|
||||
"selectRole": "Sélectionnez le rôle(s)",
|
||||
"selectRoot": "Selectionner le Dossier Racine de l'Archive",
|
||||
"selectServer": "Sélectionner un Serveur",
|
||||
"selectType": "Selectionner un Type",
|
||||
"selectVersion": "Selectionner une Version",
|
||||
"selectZipDir": "Selectionner le dossier de l'archive depuis lequel extraire les fichiers",
|
||||
@ -492,9 +570,13 @@
|
||||
"serverName": "Non du Serveur",
|
||||
"serverPath": "Chemin du Serveur",
|
||||
"serverPort": "Port du Serveur",
|
||||
"serverSelect": "Sélectionner un Serveur",
|
||||
"serverType": "Type du Serveur",
|
||||
"serverUpload": "Charger le fichier Zippé",
|
||||
"serverVersion": "Version du Serveur",
|
||||
"sizeInGB": "Taille en GB",
|
||||
"uploadButton": "Chargement",
|
||||
"uploadZip": "Charger le fichier pour l'importation du Serveur",
|
||||
"zipPath": "Chemin du Serveur"
|
||||
},
|
||||
"sidebar": {
|
||||
@ -502,6 +584,7 @@
|
||||
"credits": "Crédits",
|
||||
"dashboard": "Tableau de Bord",
|
||||
"documentation": "Documentation",
|
||||
"inApp": "Documentation Interne",
|
||||
"navigation": "Navigation",
|
||||
"newServer": "Créer un Nouveau Serveur",
|
||||
"servers": "Serveurs"
|
||||
@ -526,6 +609,7 @@
|
||||
"lastLogin": "Dernière Connexion : ",
|
||||
"lastUpdate": "Dernière Mise à Jour: ",
|
||||
"leaveBlank": "Pour modifier l'Utilisateur sans changer le MOt de Passe, laisser vide.",
|
||||
"manager": "Gestionnaire",
|
||||
"member": "Membre ?",
|
||||
"notExist": "Tu ne peux pas supprimer quelquechose qui n'existe pas !",
|
||||
"pageTitle": "Modifier Utilisateur",
|
||||
@ -534,6 +618,7 @@
|
||||
"permName": "Nom de la Permission",
|
||||
"repeat": "Confirmation du Mot de Passe",
|
||||
"roleName": "Nom du Rôle",
|
||||
"selectManager": "Sélectionner un gestionnaire pour l'Utilisateur",
|
||||
"super": "Super Utilisateur",
|
||||
"userLang": "Langue de l'Utilisateur",
|
||||
"userName": "Nom d'Utilisateur",
|
||||
@ -541,6 +626,30 @@
|
||||
"userRoles": "Rôles Utilisateur",
|
||||
"userRolesDesc": "L'utilisateur est membre de ces rôles.",
|
||||
"userSettings": "Paramètres Utilisateur",
|
||||
"userTheme": "Theme d'Interface Utilisateur",
|
||||
"uses": "Nombre d'utilisation Authorisé (-1 == Illimité)"
|
||||
},
|
||||
"webhooks": {
|
||||
"areYouSureDel": "Es-tu sûr de vouloir supprimer ce webhook ?",
|
||||
"areYouSureRun": "Es-tu sûr de vouloir tester ce webhook ?",
|
||||
"backup_server": "Sauvegarde du serveur terminée",
|
||||
"bot_name": "Nom du Bot",
|
||||
"color": "Sélectionner une couleur d'accentuation",
|
||||
"crash_detected": "Le serveur a planté",
|
||||
"edit": "Modifier",
|
||||
"enabled": "Activer",
|
||||
"jar_update": "Executable du Serveur mis à jour",
|
||||
"kill": "Serveur Interrompu",
|
||||
"name": "Nom",
|
||||
"new": "Nouveau Webhook",
|
||||
"run": "Test l'exécution du Webhook",
|
||||
"send_command": "Commande du Serveur reçue",
|
||||
"start_server": "Le serveur a démarré",
|
||||
"stop_server": "Le serveur d'est arrêté",
|
||||
"trigger": "Déclencheur",
|
||||
"type": "Type de Webhook",
|
||||
"url": "Lien URL du Webhook",
|
||||
"webhook_body": "Corps du Webhook",
|
||||
"webhooks": "Webhooks"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user