Merge branch 'enhancement/themes' into 'dev'

Add Basic Themes to Crafty

See merge request crafty-controller/crafty-4!471
This commit is contained in:
Iain Powrie 2022-10-02 19:00:04 +00:00
commit f809833aec
23 changed files with 43859 additions and 22526 deletions

View File

@ -1,7 +1,7 @@
# Changelog
## --- [4.0.15] - 2022/10/02
### New features
TBD
- Base Theme Switching (Dark, Light, Default) 🤩🎨 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/471))
### Bug fixes
- Fix traceback on basic schedule with "days" interval ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/469))
- Fix bad method call with API stdin ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/470))<br>

View File

@ -241,6 +241,7 @@ class UsersController:
email="default@example.com",
enabled: bool = True,
superuser: bool = False,
theme="default",
):
return self.users_helper.add_user(
username,
@ -249,6 +250,7 @@ class UsersController:
email=email,
enabled=enabled,
superuser=superuser,
theme=theme,
)
@staticmethod

View File

@ -43,6 +43,7 @@ class Users(BaseModel):
hints = BooleanField(default=True)
manager = IntegerField(default=None, null=True)
pfp = CharField(default="/static/assets/images/faces-clipart/pic-3.png")
theme = CharField(default="default")
class Meta:
table_name = "users"
@ -210,6 +211,7 @@ class HelperUsers:
email: t.Optional[str] = None,
enabled: bool = True,
superuser: bool = False,
theme: str = "default",
) -> str:
if password is not None:
pw_enc = self.helper.encode_pass(password)
@ -225,6 +227,7 @@ class HelperUsers:
Users.superuser: superuser,
Users.created: Helpers.get_time_as_string(),
Users.manager: manager,
Users.theme: theme,
}
).execute()
return user_id

View File

@ -399,6 +399,10 @@ class Helpers:
)
return False
@staticmethod
def get_themes():
return ["default", "dark", "light", "ronald"]
@staticmethod
def get_local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

View File

@ -878,6 +878,7 @@ class PanelHandler(BaseHandler):
page_data["user"]["roles"] = set()
page_data["user"]["hints"] = True
page_data["superuser"] = superuser
page_data["themes"] = self.helper.get_themes()
if EnumPermissionsCrafty.USER_CONFIG not in exec_user_crafty_permissions:
self.redirect(
@ -1092,6 +1093,7 @@ class PanelHandler(BaseHandler):
page_data["exec_user"] = exec_user["user_id"]
page_data["servers_all"] = self.controller.servers.get_all_defined_servers()
page_data["superuser"] = superuser
page_data["themes"] = self.helper.get_themes()
if page_data["user"]["manager"] is not None:
page_data["manager"] = self.controller.users.get_user_by_id(
page_data["user"]["manager"]
@ -2011,6 +2013,7 @@ class PanelHandler(BaseHandler):
user_id = bleach.clean(self.get_argument("id", None))
user = self.controller.users.get_user_by_id(user_id)
username = bleach.clean(self.get_argument("username", None).lower())
theme = bleach.clean(self.get_argument("theme", "default"))
if (
username != self.controller.users.get_user_by_id(user_id)["username"]
and username in self.controller.users.get_all_usernames()
@ -2077,6 +2080,7 @@ class PanelHandler(BaseHandler):
"email": email,
"lang": lang,
"hints": hints,
"theme": theme,
}
self.controller.users.update_user(user_id, user_data=user_data)
@ -2113,6 +2117,7 @@ class PanelHandler(BaseHandler):
"lang": lang,
"superuser": superuser,
"hints": hints,
"theme": theme,
}
user_crafty_data = {
"permissions_mask": permissions_mask,
@ -2224,6 +2229,7 @@ class PanelHandler(BaseHandler):
password1 = bleach.clean(self.get_argument("password1", None))
email = bleach.clean(self.get_argument("email", "default@example.com"))
enabled = int(float(self.get_argument("enabled", "0")))
theme = bleach.clean(self.get_argument("theme"), "default")
hints = True
lang = bleach.clean(
self.get_argument("lang", self.helper.get_setting("language"))
@ -2279,6 +2285,7 @@ class PanelHandler(BaseHandler):
email=email,
enabled=enabled,
superuser=new_superuser,
theme=theme,
)
user_data = {"roles": roles, "lang": lang, "hints": True}
user_crafty_data = {

View File

@ -105,6 +105,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
permissions = data.get("permissions", None)
roles = data.get("roles", None)
hints = data.get("hints", True)
theme = data.get("theme", "default")
if username.lower() in ["system", ""]:
return self.finish_json(
@ -155,6 +156,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
email,
enabled,
new_superuser,
theme,
)
self.controller.users.update_user(
user_id,

View File

@ -1,11 +1,11 @@
.select-css option {
background-color: #1C1E2F;
color: white
background-color: var(--deep-bg);
color: var(--base-text)
}
.select-css option:checked {
background-color: #1C1E2F;
color: white
background-color: var(--deep-bg);
color: var(--base-text)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,68 +3,122 @@
* License - https://fontawesome.com/license (Commercial License)
*/
svg:not(:root).svg-inline--fa {
overflow: visible; }
overflow: visible;
}
.svg-inline--fa {
display: inline-block;
font-size: inherit;
height: 1em;
overflow: visible;
vertical-align: -.125em; }
.svg-inline--fa.fa-lg {
vertical-align: -.225em; }
.svg-inline--fa.fa-w-1 {
width: 0.0625em; }
.svg-inline--fa.fa-w-2 {
width: 0.125em; }
.svg-inline--fa.fa-w-3 {
width: 0.1875em; }
.svg-inline--fa.fa-w-4 {
width: 0.25em; }
.svg-inline--fa.fa-w-5 {
width: 0.3125em; }
.svg-inline--fa.fa-w-6 {
width: 0.375em; }
.svg-inline--fa.fa-w-7 {
width: 0.4375em; }
.svg-inline--fa.fa-w-8 {
width: 0.5em; }
.svg-inline--fa.fa-w-9 {
width: 0.5625em; }
.svg-inline--fa.fa-w-10 {
width: 0.625em; }
.svg-inline--fa.fa-w-11 {
width: 0.6875em; }
.svg-inline--fa.fa-w-12 {
width: 0.75em; }
.svg-inline--fa.fa-w-13 {
width: 0.8125em; }
.svg-inline--fa.fa-w-14 {
width: 0.875em; }
.svg-inline--fa.fa-w-15 {
width: 0.9375em; }
.svg-inline--fa.fa-w-16 {
width: 1em; }
.svg-inline--fa.fa-w-17 {
width: 1.0625em; }
.svg-inline--fa.fa-w-18 {
width: 1.125em; }
.svg-inline--fa.fa-w-19 {
width: 1.1875em; }
.svg-inline--fa.fa-w-20 {
width: 1.25em; }
.svg-inline--fa.fa-pull-left {
margin-right: .3em;
width: auto; }
.svg-inline--fa.fa-pull-right {
margin-left: .3em;
width: auto; }
.svg-inline--fa.fa-border {
height: 1.5em; }
.svg-inline--fa.fa-li {
width: 2em; }
.svg-inline--fa.fa-fw {
width: 1.25em; }
vertical-align: -.125em;
}
.svg-inline--fa.fa-lg {
vertical-align: -.225em;
}
.svg-inline--fa.fa-w-1 {
width: 0.0625em;
}
.svg-inline--fa.fa-w-2 {
width: 0.125em;
}
.svg-inline--fa.fa-w-3 {
width: 0.1875em;
}
.svg-inline--fa.fa-w-4 {
width: 0.25em;
}
.svg-inline--fa.fa-w-5 {
width: 0.3125em;
}
.svg-inline--fa.fa-w-6 {
width: 0.375em;
}
.svg-inline--fa.fa-w-7 {
width: 0.4375em;
}
.svg-inline--fa.fa-w-8 {
width: 0.5em;
}
.svg-inline--fa.fa-w-9 {
width: 0.5625em;
}
.svg-inline--fa.fa-w-10 {
width: 0.625em;
}
.svg-inline--fa.fa-w-11 {
width: 0.6875em;
}
.svg-inline--fa.fa-w-12 {
width: 0.75em;
}
.svg-inline--fa.fa-w-13 {
width: 0.8125em;
}
.svg-inline--fa.fa-w-14 {
width: 0.875em;
}
.svg-inline--fa.fa-w-15 {
width: 0.9375em;
}
.svg-inline--fa.fa-w-16 {
width: 1em;
}
.svg-inline--fa.fa-w-17 {
width: 1.0625em;
}
.svg-inline--fa.fa-w-18 {
width: 1.125em;
}
.svg-inline--fa.fa-w-19 {
width: 1.1875em;
}
.svg-inline--fa.fa-w-20 {
width: 1.25em;
}
.svg-inline--fa.fa-pull-left {
margin-right: .3em;
width: auto;
}
.svg-inline--fa.fa-pull-right {
margin-left: .3em;
width: auto;
}
.svg-inline--fa.fa-border {
height: 1.5em;
}
.svg-inline--fa.fa-li {
width: 2em;
}
.svg-inline--fa.fa-fw {
width: 1.25em;
}
.fa-layers svg.svg-inline--fa {
bottom: 0;
@ -72,7 +126,8 @@ svg:not(:root).svg-inline--fa {
margin: auto;
position: absolute;
right: 0;
top: 0; }
top: 0;
}
.fa-layers {
display: inline-block;
@ -80,30 +135,36 @@ svg:not(:root).svg-inline--fa {
position: relative;
text-align: center;
vertical-align: -.125em;
width: 1em; }
.fa-layers svg.svg-inline--fa {
-webkit-transform-origin: center center;
transform-origin: center center; }
width: 1em;
}
.fa-layers-text, .fa-layers-counter {
.fa-layers svg.svg-inline--fa {
-webkit-transform-origin: center center;
transform-origin: center center;
}
.fa-layers-text,
.fa-layers-counter {
display: inline-block;
position: absolute;
text-align: center; }
text-align: center;
}
.fa-layers-text {
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
-webkit-transform-origin: center center;
transform-origin: center center; }
transform-origin: center center;
}
.fa-layers-counter {
background-color: #ff253a;
border-radius: 1em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #fff;
box-sizing: border-box;
var(--base-text);
height: 1.5em;
line-height: 1;
max-width: 5em;
@ -114,18 +175,20 @@ svg:not(:root).svg-inline--fa {
text-overflow: ellipsis;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
transform-origin: top right;
}
.fa-layers-bottom-right {
bottom: 0;
right: 0;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom right;
transform-origin: bottom right; }
transform-origin: bottom right;
}
.fa-layers-bottom-left {
bottom: 0;
@ -133,164 +196,207 @@ svg:not(:root).svg-inline--fa {
right: auto;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom left;
transform-origin: bottom left; }
transform-origin: bottom left;
}
.fa-layers-top-right {
right: 0;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
transform-origin: top right;
}
.fa-layers-top-left {
left: 0;
right: auto;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top left;
transform-origin: top left; }
transform-origin: top left;
}
.fa-lg {
font-size: 1.33333em;
line-height: 0.75em;
vertical-align: -.0667em; }
vertical-align: -.0667em;
}
.fa-xs {
font-size: .75em; }
font-size: .75em;
}
.fa-sm {
font-size: .875em; }
font-size: .875em;
}
.fa-1x {
font-size: 1em; }
font-size: 1em;
}
.fa-2x {
font-size: 2em; }
font-size: 2em;
}
.fa-3x {
font-size: 3em; }
font-size: 3em;
}
.fa-4x {
font-size: 4em; }
font-size: 4em;
}
.fa-5x {
font-size: 5em; }
font-size: 5em;
}
.fa-6x {
font-size: 6em; }
font-size: 6em;
}
.fa-7x {
font-size: 7em; }
font-size: 7em;
}
.fa-8x {
font-size: 8em; }
font-size: 8em;
}
.fa-9x {
font-size: 9em; }
font-size: 9em;
}
.fa-10x {
font-size: 10em; }
font-size: 10em;
}
.fa-fw {
text-align: center;
width: 1.25em; }
width: 1.25em;
}
.fa-ul {
list-style-type: none;
margin-left: 2.5em;
padding-left: 0; }
.fa-ul > li {
position: relative; }
padding-left: 0;
}
.fa-ul>li {
position: relative;
}
.fa-li {
left: -2em;
position: absolute;
text-align: center;
width: 2em;
line-height: inherit; }
line-height: inherit;
}
.fa-border {
border: solid 0.08em #eee;
border-radius: .1em;
padding: .2em .25em .15em; }
padding: .2em .25em .15em;
}
.fa-pull-left {
float: left; }
float: left;
}
.fa-pull-right {
float: right; }
float: right;
}
.fa.fa-pull-left,
.fas.fa-pull-left,
.far.fa-pull-left,
.fal.fa-pull-left,
.fab.fa-pull-left {
margin-right: .3em; }
margin-right: .3em;
}
.fa.fa-pull-right,
.fas.fa-pull-right,
.far.fa-pull-right,
.fal.fa-pull-right,
.fab.fa-pull-right {
margin-left: .3em; }
margin-left: .3em;
}
.fa-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear; }
animation: fa-spin 2s infinite linear;
}
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8); }
animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
transform: rotate(360deg);
}
}
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
transform: rotate(360deg);
}
}
.fa-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
transform: rotate(90deg); }
transform: rotate(90deg);
}
.fa-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
transform: rotate(180deg); }
transform: rotate(180deg);
}
.fa-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
transform: rotate(270deg);
}
.fa-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
transform: scale(-1, 1); }
transform: scale(-1, 1);
}
.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
transform: scale(1, -1); }
transform: scale(1, -1);
}
.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {
.fa-flip-both,
.fa-flip-horizontal.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(-1, -1);
transform: scale(-1, -1); }
transform: scale(-1, -1);
}
:root .fa-rotate-90,
:root .fa-rotate-180,
@ -299,13 +405,15 @@ svg:not(:root).svg-inline--fa {
:root .fa-flip-vertical,
:root .fa-flip-both {
-webkit-filter: none;
filter: none; }
filter: none;
}
.fa-stack {
display: inline-block;
height: 2em;
position: relative;
width: 2.5em; }
width: 2.5em;
}
.fa-stack-1x,
.fa-stack-2x {
@ -314,18 +422,22 @@ svg:not(:root).svg-inline--fa {
margin: auto;
position: absolute;
right: 0;
top: 0; }
top: 0;
}
.svg-inline--fa.fa-stack-1x {
height: 1em;
width: 1.25em; }
width: 1.25em;
}
.svg-inline--fa.fa-stack-2x {
height: 2em;
width: 2.5em; }
width: 2.5em;
}
.fa-inverse {
color: #fff; }
var(--base-text);
}
.sr-only {
border: 0;
@ -335,37 +447,46 @@ svg:not(:root).svg-inline--fa {
overflow: hidden;
padding: 0;
position: absolute;
width: 1px; }
width: 1px;
}
.sr-only-focusable:active, .sr-only-focusable:focus {
.sr-only-focusable:active,
.sr-only-focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto; }
width: auto;
}
.svg-inline--fa .fa-primary {
fill: var(--fa-primary-color, currentColor);
opacity: 1;
opacity: var(--fa-primary-opacity, 1); }
opacity: var(--fa-primary-opacity, 1);
}
.svg-inline--fa .fa-secondary {
fill: var(--fa-secondary-color, currentColor);
opacity: 0.4;
opacity: var(--fa-secondary-opacity, 0.4); }
opacity: var(--fa-secondary-opacity, 0.4);
}
.svg-inline--fa.fa-swap-opacity .fa-primary {
opacity: 0.4;
opacity: var(--fa-secondary-opacity, 0.4); }
opacity: var(--fa-secondary-opacity, 0.4);
}
.svg-inline--fa.fa-swap-opacity .fa-secondary {
opacity: 1;
opacity: var(--fa-primary-opacity, 1); }
opacity: var(--fa-primary-opacity, 1);
}
.svg-inline--fa mask .fa-primary,
.svg-inline--fa mask .fa-secondary {
fill: black; }
fill: black;
}
.fad.fa-inverse {
color: #fff; }
var(--base-text);
}

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{ data['lang_page'] }}">
<html lang="{{ data['lang_page'] }}" class="{{data['user_data'].get('theme', 'default')}}">
<head>
<!-- Required meta tags -->
@ -54,7 +54,7 @@
</head>
<body class="dark-theme">
<body>
<div class="container-scroller">
<!-- partial:partials/_navbar.html -->
<nav class="navbar default-layout col-lg-12 col-12 p-0 fixed-top d-flex flex-row">
@ -127,7 +127,7 @@
width: 180px;
margin-left: 10px;
margin-right: 10px;
background: #282a40;
background: var(--card-banner-bg);
transition: right 0.75s, opacity 0.75s, top 0.75s;
right: -6rem;
opacity: 0.1;

View File

@ -152,13 +152,13 @@
position: absolute;
bottom: 0;
left: 0;
border-bottom: var(--table-border-width) solid #383e5d;
border-bottom: var(--table-border-width) solid var(--outline);
transition: border-bottom-color 500ms;
padding-bottom: 5px;
user-select: none;
}
table.rotate-table > tbody td {
border-right: var(--table-border-width) solid #383e5d;
border-right: var(--table-border-width) solid var(--outline);
/* make sure this is at least as wide as sqrt(2) * height of the tallest letter in your font or the headers will overlap each other*/
min-width: 30px;
padding-top: 2px;

View File

@ -121,6 +121,19 @@ data['lang']) }}{% end %}
{% end %}
</select>
</div>
<div class="form-group">
<label class="form-label" for="theme">{{ translate('userConfig', 'userTheme', data['lang'])
}}</label>
<select class="form-select form-control form-control-lg select-css" id="language"
name="theme" form="user_form">
<option value="{{data['user'].get('theme', 'default')}}">{{data['user'].get('theme', 'default')}}</option>
{% for theme in data['themes'] %}
{% if theme != data['user'].get('theme', 'default') %}
<option value="{{theme}}">{{theme}}</option>
{% end %}
{% end %}
</select>
</div>
{% if data['superuser'] %}
<div class="form-group">
<label class="form-label" for="manager">{{ translate('userConfig', 'selectManager',

View File

@ -42,7 +42,6 @@
<div class="col-md-6 col-sm-12">
<style>
.playerItem {
background: #1c1e2f;
padding: 1rem;
display: flex;
flex-flow: row wrap;

View File

@ -42,7 +42,7 @@
<div class="col-md-12">
<div class="input-group">
<div id="virt_console" class=""
style="font-size: .8em; padding: 5px 10px; border: 1px solid #383e5d; background-color:#2a2c44;height:500px; overflow: scroll;">
style="font-size: .8em; padding: 5px 10px; border: 1px solid var(--outline); background-color:var(--card-banner-bg);height:500px; overflow: scroll;">
</div>
</div>
<br />

View File

@ -14,7 +14,8 @@
<div class="col-12">
<div class="page-header">
<h4 class="page-title">
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{ data['server_stats']['server_id']['server_name'] }}
{{ translate('serverDetails', 'serverDetails', data['lang']) }} - {{
data['server_stats']['server_id']['server_name'] }}
<br />
<small>UUID: {{ data['server_stats']['server_id']['server_uuid'] }}</small>
</h4>
@ -40,16 +41,21 @@
</span>
<div class="col-md-12">
<button id="to-bottom" style="visibility: hidden; float: right;" class="btn btn-outline-success">{{ translate('serverDetails', 'reset', data['lang']) }}</button>
<button id="to-bottom" style="visibility: hidden; float: right;" class="btn btn-outline-success">{{
translate('serverDetails', 'reset', data['lang']) }}</button>
<br />
<br />
<div class="input-group">
<div id="virt_console" class="" style="width: 100%; font-size: .8em; padding: 5px 10px; border: 1px solid #383e5d; background-color:#2a2c44;height:500px; overflow: scroll;"></div>
<div id="virt_console" class=""
style="width: 100%; font-size: .8em; padding: 5px 10px; border: 1px solid var(--outline); background-color:var(--card-banner-bg);height:500px; overflow: scroll;">
</div>
</div>
<br />
<div style="gap: 0.5rem;" class="input-group flex-wrap">
<input style="min-width: 10rem;" type="text" class="form-control" id="server_command" name="server_command" placeholder="{{ translate('serverTerm', 'commandInput', data['lang']) }}" autofocus="">
<input style="min-width: 10rem;" type="text" class="form-control" id="server_command"
name="server_command" placeholder="{{ translate('serverTerm', 'commandInput', data['lang']) }}"
autofocus="">
<span class="input-group-btn ml-5">
<button id="submit" class="btn btn-sm btn-info" type="button">{{ translate('serverTerm', 'sendCommand',
data['lang']) }}</button>
@ -57,30 +63,60 @@
</div>
{% if data['permissions']['Commands'] in data['user_permissions'] %}
{% if data['server_stats']['updating']%}
<div id="update_control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 7rem;" class="btn btn-warning m-1 flex-grow-1 disabled"><i
class="fa fa-spinner fa-spin"></i>&nbsp;{{ translate('serverTerm', 'updating', data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
<div id="update_control_buttons"
class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0"
style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 7rem;"
class="btn btn-warning m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i>&nbsp;{{
translate('serverTerm', 'updating', data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;"
class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart',
data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;"
class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang'])
}}</button>
</div>
{% elif data['waiting_start'] %}
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 7rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled" data-toggle="tooltip" title="{{ translate('serverTerm', 'delay-explained', data['lang'])}}">{{ translate('serverTerm', 'starting', data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
<div id="control_buttons"
class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0"
style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 7rem; white-space: nowrap;"
class="btn btn-secondary m-1 flex-grow-1 disabled" data-toggle="tooltip"
title="{{ translate('serverTerm', 'delay-explained', data['lang'])}}">{{ translate('serverTerm',
'starting', data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;"
class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart',
data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;"
class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang'])
}}</button>
</div>
{% elif data['importing'] %}
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;" class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'importing',
<div id="control_buttons"
class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0"
style="visibility: visible">
<button onclick="" id="start-btn" style="max-width: 12rem; white-space: nowrap;"
class="btn btn-secondary m-1 flex-grow-1 disabled"><i class="fa fa-spinner fa-spin"></i> {{
translate('serverTerm', 'importing',
data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
<button onclick="" id="restart-btn" style="max-width: 7rem;"
class="btn btn-outline-primary m-1 flex-grow-1 disabled">{% raw translate('serverTerm', 'restart',
data['lang']) %}</button>
<button onclick="" id="stop-btn" style="max-width: 7rem;"
class="btn btn-danger m-1 flex-grow-1 disabled">{{ translate('serverTerm', 'stop', data['lang'])
}}</button>
</div>
{% else %}
<div id="control_buttons" class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0" style="visibility: visible">
<button onclick="send_command(serverId, 'start_server');" id="start-btn" style="max-width: 7rem;" class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start', data['lang']) }}</button>
<button onclick="send_command(serverId, 'restart_server');" id="restart-btn" style="max-width: 7rem;" class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate('serverTerm', 'restart', data['lang']) %}</button>
<button onclick="send_command(serverId, 'stop_server');" id="stop-btn" style="max-width: 7rem;" class="btn btn-danger m-1 flex-grow-1">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
<div id="control_buttons"
class="mt-4 flex-wrap d-flex justify-content-between justify-content-md-center align-items-center px-5 px-md-0"
style="visibility: visible">
<button onclick="send_command(serverId, 'start_server');" id="start-btn" style="max-width: 7rem;"
class="btn btn-primary m-1 flex-grow-1">{{ translate('serverTerm', 'start', data['lang']) }}</button>
<button onclick="send_command(serverId, 'restart_server');" id="restart-btn" style="max-width: 7rem;"
class="btn btn-outline-primary m-1 flex-grow-1">{% raw translate('serverTerm', 'restart', data['lang'])
%}</button>
<button onclick="send_command(serverId, 'stop_server');" id="stop-btn" style="max-width: 7rem;"
class="btn btn-danger m-1 flex-grow-1">{{ translate('serverTerm', 'stop', data['lang']) }}</button>
</div>
{% end %}
{% end %}
@ -196,11 +232,11 @@
function new_line_handler(data) {
$('#virt_console').append(data.line)
const elem = document.getElementById('virt_console');
try{
try {
if (!scrolled) {
scrollConsole();
}
}catch{
} catch {
scrollConsole();
}
}
@ -305,18 +341,18 @@
if (Math.round(elem[0].scrollHeight - elem.scrollTop()) <= elem.outerHeight()) {
document.getElementById("to-bottom").style.visibility = "hidden";
scrolled = false;
}else{
} else {
document.getElementById("to-bottom").style.visibility = "visible";
scrolled = true;
}
}
const scrollToBottom = (id) => {
const element = $(`#virt_console`);
element.animate({
const element = $(`#virt_console`);
element.animate({
scrollTop: element.prop("scrollHeight")
}, 500);
}
}, 500);
}
$(document).ready(() => {
var scrolled = false;

View File

@ -8,7 +8,7 @@
{% block content %}
<!-- View for Large screen -->
<div class="row justify-content-center">
<div class="content-wrapper col-md login-modal d-none d-sm-block" style="background-color: #222437;">
<div class="content-wrapper col-md login-modal d-none d-sm-block" style="background-color: var(--dropdown-bg);">
<img src="/static/assets/images/logo_long.png" style='width: 25%; margin-left: 38%;'>
<hr />
<div class="table-responsive">
@ -40,7 +40,8 @@
</td>
<td id="server_motd_{{ server['stats']['server_id']['server_id'] }}">
{% if server['stats']['desc'] != 'False' %}
<img src="/static/assets/images/pack.png" alt="icon" style="-webkit-filter:grayscale(100%); filter:grayscale(100%)" />
<img src="/static/assets/images/pack.png" alt="icon"
style="-webkit-filter:grayscale(100%); filter:grayscale(100%)" />
<span id="input_motd_{{ server['stats']['server_id']['server_id'] }}" class="input_motd">{{
server['stats']['desc'] }}</span> <br />
{% end %}
@ -94,21 +95,25 @@
<h2 class="mb-0 container overflow-hidden">
<div class="row">
<div class="col-8 mx-0 px-0">
<a id="m_server_name_{{ server['stats']['server_id']['server_id'] }}" class="btn btn-link d-flex justify-content-center" type="button" data-toggle="collapse"
data-target="#collapse-{{server['server_data']['server_id']}}" aria-expanded="false"
aria-controls="collapse-{{server['server_data']['server_id']}}">
<a id="m_server_name_{{ server['stats']['server_id']['server_id'] }}"
class="btn btn-link d-flex justify-content-center" type="button" data-toggle="collapse"
data-target="#collapse-{{server['server_data']['server_id']}}" aria-expanded="false"
aria-controls="collapse-{{server['server_data']['server_id']}}">
<i class="fas fa-server"></i>
{{ server['server_data']['server_name'] }}
</a>
</div>
<div class="col-4 mx-0 px-0">
<a id="m_server_online_status_{{ server['stats']['server_id']['server_id'] }}" class="btn btn-link d-flex justify-content-center" type="button">
<a id="m_server_online_status_{{ server['stats']['server_id']['server_id'] }}"
class="btn btn-link d-flex justify-content-center" type="button">
{% if server['stats']['running'] %}
<div id="m_server_players_{{ server['stats']['server_id']['server_id'] }}">
<span class="text-success"><i class="fas fa-signal"></i> {{ server['stats']['online'] }} / {{ server['stats']['max'] }}</span>
<span class="text-success"><i class="fas fa-signal"></i> {{ server['stats']['online'] }} / {{
server['stats']['max'] }}</span>
</div>
{% else %}
<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang']) }}</span>
<span class="text-danger"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline',
data['lang']) }}</span>
{% end %}
</a>
</div>
@ -117,12 +122,13 @@
</div>
<div id="collapse-{{server['server_data']['server_id']}}" class="collapse"
aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers">
<div class="card-body">
{% if server['stats']['int_ping_results'] != 'False' %}
<div id="m_server_motd_{{ server['stats']['server_id']['server_id'] }}" class="media">
{% if server['stats']['desc'] != 'False' %}
<img src="/static/assets/images/pack.png" class="w-25 mr-3" alt="icon" style="-webkit-filter:grayscale(100%); filter:grayscale(100%);" />
<img src="/static/assets/images/pack.png" class="w-25 mr-3" alt="icon"
style="-webkit-filter:grayscale(100%); filter:grayscale(100%);" />
{% end %}
<div class="media-body">
{% if server['stats']['desc'] != 'False' %}
@ -143,7 +149,8 @@
<div class="row">
<div class="col-12">
<div id="m_server_motd_{{ server['stats']['server_id']['server_id'] }}">
<span class="text-warning"><i class="fas fa-exclamation-triangle"></i> Crafty can't get infos from this Server </span>
<span class="text-warning"><i class="fas fa-exclamation-triangle"></i> Crafty can't get infos from
this Server </span>
</div>
<div id="m_server_version_{{ server['stats']['server_id']['server_id'] }}"></div>
</div>

View File

@ -343,7 +343,7 @@
z-index: 200;
margin-top: 4px;
position: absolute;
background-color: #2a2c44;
background-color: var(--card-banner-bg);
}
.menu-option {

View File

@ -457,7 +457,7 @@
z-index: 200;
margin-top: 4px;
position: absolute;
background-color: #2a2c44;
background-color: var(--card-banner-bg);
}
.menu-option {

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("users", theme=peewee.CharField(default="default"))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("users", ["theme"])
"""
Write your rollback migrations here.
"""

View File

@ -565,6 +565,7 @@
"roleName": "Role Name",
"super": "Super User",
"userLang": "User Language",
"userTheme": "UI Theme",
"userName": "User Name",
"userNameDesc": "What do you want to call this user?",
"userRoles": "User Roles",