diff --git a/.gitlab/lint.yml b/.gitlab/lint.yml
index f335dea8..101e26b2 100644
--- a/.gitlab/lint.yml
+++ b/.gitlab/lint.yml
@@ -44,7 +44,7 @@ black:
# Code Climate/Quality Checking [https://pylint.pycqa.org/en/latest/]
pylint:
stage: lint
- image: registry.gitlab.com/pipeline-components/pylint:latest
+ image: registry.gitlab.com/pipeline-components/pylint:0.21.1
tags:
- docker
rules:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 627014a0..7054c1b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,23 @@
# Changelog
+## --- [4.2.1] - 2023/11/01
+### Bug fixes
+- Fix logic issue with `get_files` API permissions check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/654))
+- Fix notifications not showing up/being reset #298 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/660))
+- Fix users not being able to be deleted since the prompt fails to display ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/661))
+- Fix duplicate function naming on dashboard ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/662))
+### Tweaks
+- Auto refresh Crafty Announcements on 30m interval ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/653))
+- Improve Crafty toggle buttons and Webhooks page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/656))
+### Lang
+- Update `zh_CN` lang file ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/652))
+- Update `es_ES` lang file ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/655))
+- Clean up wording in `pl_PL` lang file ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/656))
+- Add `de_DE`, `es_ES` `fr_FR`, `lol_EN`, `lv_LV`, `nl_BE` `pl_PL` & `zh_CN` translations for !656 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/656))
+### Docs
+- [(New) Server Webhook Documentation](https://docs.craftycontrol.com/pages/user-guide/webhooks/)
+- [(Edit) Image Context in Windows Service - Install steps, with slight wording improvement](https://docs.craftycontrol.com/pages/getting-started/installation/windows/#install-steps)
+
+
## --- [4.2.0] - 2023/10/18
### New features
- Finish and Activate Arcadia notification backend ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/621) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/626) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/632))
diff --git a/README.md b/README.md
index 5d2379c8..a805eca7 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
[![Crafty Logo](app/frontend/static/assets/images/logo_long.svg)](https://craftycontrol.com)
-# Crafty Controller 4.2.0
+# Crafty Controller 4.2.1
> Python based Control Panel for your Minecraft Server
## What is Crafty Controller?
diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py
index ba9c5a28..62ce8819 100644
--- a/app/classes/shared/helpers.py
+++ b/app/classes/shared/helpers.py
@@ -578,16 +578,19 @@ class Helpers:
return version_data
def get_announcements(self):
- data = []
try:
+ data = []
response = requests.get("https://craftycontrol.com/notify", timeout=2)
data = json.loads(response.content)
+ if self.update_available:
+ data.append(self.update_available)
+ return data
except Exception as e:
logger.error(f"Failed to fetch notifications with error: {e}")
-
- if self.update_available:
- data.append(self.update_available)
- return data
+ if self.update_available:
+ data = [self.update_available]
+ else:
+ return False
def get_version_string(self):
version_data = self.get_version()
diff --git a/app/classes/web/routes/api/crafty/announcements/index.py b/app/classes/web/routes/api/crafty/announcements/index.py
index 409aceed..75f00f16 100644
--- a/app/classes/web/routes/api/crafty/announcements/index.py
+++ b/app/classes/web/routes/api/crafty/announcements/index.py
@@ -29,6 +29,14 @@ class ApiAnnounceIndexHandler(BaseApiHandler):
) = auth_data
data = self.helper.get_announcements()
+ if not data:
+ return self.finish_json(
+ 424,
+ {
+ "status": "error",
+ "data": "Failed to get announcements",
+ },
+ )
cleared = str(
self.controller.users.get_user_by_id(auth_data[4]["user_id"])[
"cleared_notifs"
@@ -84,6 +92,14 @@ class ApiAnnounceIndexHandler(BaseApiHandler):
},
)
announcements = self.helper.get_announcements()
+ if not announcements:
+ return self.finish_json(
+ 424,
+ {
+ "status": "error",
+ "data": "Failed to get current announcements",
+ },
+ )
res = [d.get("id", None) for d in announcements]
cleared_notifs = str(
self.controller.users.get_user_by_id(auth_data[4]["user_id"])[
diff --git a/app/classes/web/routes/api/servers/server/files.py b/app/classes/web/routes/api/servers/server/files.py
index 9ed720ac..37007acd 100644
--- a/app/classes/web/routes/api/servers/server/files.py
+++ b/app/classes/web/routes/api/servers/server/files.py
@@ -86,7 +86,7 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id
)
- or EnumPermissionsServer.BACKUP
+ and EnumPermissionsServer.BACKUP
not in self.controller.server_perms.get_user_id_permissions_list(
auth_data[4]["user_id"], server_id
)
diff --git a/app/classes/web/webhooks/webhook_factory.py b/app/classes/web/webhooks/webhook_factory.py
index 608bf4e5..9fe2c752 100644
--- a/app/classes/web/webhooks/webhook_factory.py
+++ b/app/classes/web/webhooks/webhook_factory.py
@@ -21,7 +21,6 @@ class WebhookFactory:
"Mattermost": MattermostWebhook,
"Slack": SlackWebhook,
"Teams": TeamsWebhook,
- # "Custom",
}
@classmethod
diff --git a/app/config/version.json b/app/config/version.json
index 6c1274e0..47d57814 100644
--- a/app/config/version.json
+++ b/app/config/version.json
@@ -1,5 +1,5 @@
{
"major": 4,
"minor": 2,
- "sub": 0
+ "sub": 1
}
diff --git a/app/frontend/static/assets/css/crafty-toggle-btn.css b/app/frontend/static/assets/css/crafty-toggle-btn.css
new file mode 100644
index 00000000..94ccaa7b
--- /dev/null
+++ b/app/frontend/static/assets/css/crafty-toggle-btn.css
@@ -0,0 +1,447 @@
+/**************************************************************/
+/* CSS for Toggle Buttons */
+/**************************************************************/
+.btn-toggle {
+ margin: 0 4rem;
+ padding: 0;
+ position: relative;
+ border: none;
+ height: 1.5rem;
+ width: 3rem;
+ border-radius: 1.5rem;
+ color: #6b7381;
+ background: #bdc1c8;
+}
+
+.btn-toggle:focus,
+.btn-toggle.focus,
+.btn-toggle:focus.active,
+.btn-toggle.focus.active {
+ outline: none;
+}
+
+.btn-toggle:before,
+.btn-toggle:after {
+ line-height: 1.5rem;
+ width: 4rem;
+ text-align: center;
+ font-weight: 600;
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ position: absolute;
+ bottom: 0;
+ transition: opacity 0.25s;
+}
+
+.btn-toggle:before {
+ content: 'Off';
+ left: -4rem;
+}
+
+.btn-toggle:after {
+ content: 'On';
+ right: -4rem;
+ opacity: 0.5;
+}
+
+.btn-toggle>.handle {
+ position: absolute;
+ top: 0.1875rem;
+ left: 0.1875rem;
+ width: 1.125rem;
+ height: 1.125rem;
+ border-radius: 1.125rem;
+ background: #fff;
+ transition: left 0.25s;
+}
+
+.btn-toggle.active {
+ transition: background-color 0.25s;
+}
+
+.btn-toggle.active>.handle {
+ left: 1.6875rem;
+ transition: left 0.25s;
+}
+
+.btn-toggle.active:before {
+ opacity: 0.5;
+}
+
+.btn-toggle.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-sm:before,
+.btn-toggle.btn-sm:after {
+ line-height: -0.5rem;
+ color: #fff;
+ letter-spacing: 0.75px;
+ left: 0.4125rem;
+ width: 2.325rem;
+}
+
+.btn-toggle.btn-sm:before {
+ text-align: right;
+}
+
+.btn-toggle.btn-sm:after {
+ text-align: left;
+ opacity: 0;
+}
+
+.btn-toggle.btn-sm.active:before {
+ opacity: 0;
+}
+
+.btn-toggle.btn-sm.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-xs:before,
+.btn-toggle.btn-xs:after {
+ display: none;
+}
+
+.btn-toggle:before,
+.btn-toggle:after {
+ color: #6b7381;
+}
+
+.btn-toggle.active {
+ background-color: #29b5a8;
+}
+
+.btn-toggle.btn-lg {
+ margin: 0 5rem;
+ padding: 0;
+ position: relative;
+ border: none;
+ height: 2.5rem;
+ width: 5rem;
+ border-radius: 2.5rem;
+}
+
+.btn-toggle.btn-lg:focus,
+.btn-toggle.btn-lg.focus,
+.btn-toggle.btn-lg:focus.active,
+.btn-toggle.btn-lg.focus.active {
+ outline: none;
+}
+
+.btn-toggle.btn-lg:before,
+.btn-toggle.btn-lg:after {
+ line-height: 2.5rem;
+ width: 5rem;
+ text-align: center;
+ font-weight: 600;
+ font-size: 1rem;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ position: absolute;
+ bottom: 0;
+ transition: opacity 0.25s;
+}
+
+.btn-toggle.btn-lg:before {
+ content: 'Off';
+ left: -5rem;
+}
+
+.btn-toggle.btn-lg:after {
+ content: 'On';
+ right: -5rem;
+ opacity: 0.5;
+}
+
+.btn-toggle.btn-lg>.handle {
+ position: absolute;
+ top: 0.3125rem;
+ left: 0.3125rem;
+ width: 1.875rem;
+ height: 1.875rem;
+ border-radius: 1.875rem;
+ background: #fff;
+ transition: left 0.25s;
+}
+
+.btn-toggle.btn-lg.active {
+ transition: background-color 0.25s;
+}
+
+.btn-toggle.btn-lg.active>.handle {
+ left: 2.8125rem;
+ transition: left 0.25s;
+}
+
+.btn-toggle.btn-lg.active:before {
+ opacity: 0.5;
+}
+
+.btn-toggle.btn-lg.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-lg.btn-sm:before,
+.btn-toggle.btn-lg.btn-sm:after {
+ line-height: 0.5rem;
+ color: #fff;
+ letter-spacing: 0.75px;
+ left: 0.6875rem;
+ width: 3.875rem;
+}
+
+.btn-toggle.btn-lg.btn-sm:before {
+ text-align: right;
+}
+
+.btn-toggle.btn-lg.btn-sm:after {
+ text-align: left;
+ opacity: 0;
+}
+
+.btn-toggle.btn-lg.btn-sm.active:before {
+ opacity: 0;
+}
+
+.btn-toggle.btn-lg.btn-sm.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-lg.btn-xs:before,
+.btn-toggle.btn-lg.btn-xs:after {
+ display: none;
+}
+
+.btn-toggle.btn-sm {
+ margin: 0 0.5rem;
+ padding: 0;
+ position: relative;
+ border: none;
+ height: 1.5rem;
+ width: 3rem;
+ border-radius: 1.5rem;
+}
+
+.btn-toggle.btn-sm:focus,
+.btn-toggle.btn-sm.focus,
+.btn-toggle.btn-sm:focus.active,
+.btn-toggle.btn-sm.focus.active {
+ outline: none;
+}
+
+.btn-toggle.btn-sm:before,
+.btn-toggle.btn-sm:after {
+ line-height: 1.5rem;
+ width: 0.5rem;
+ text-align: center;
+ font-weight: 600;
+ font-size: 0.55rem;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ position: absolute;
+ bottom: 0;
+ transition: opacity 0.25s;
+}
+
+.btn-toggle.btn-sm:before {
+ content: 'Off';
+ left: -0.5rem;
+}
+
+.btn-toggle.btn-sm:after {
+ content: 'On';
+ right: -0.5rem;
+ opacity: 0.5;
+}
+
+.btn-toggle.btn-sm>.handle {
+ position: absolute;
+ top: 0.1875rem;
+ left: 0.1875rem;
+ width: 1.125rem;
+ height: 1.125rem;
+ border-radius: 1.125rem;
+ background: #fff;
+ transition: left 0.25s;
+}
+
+.btn-toggle.btn-sm.active {
+ transition: background-color 0.25s;
+}
+
+.btn-toggle.btn-sm.active>.handle {
+ left: 1.6875rem;
+ transition: left 0.25s;
+}
+
+.btn-toggle.btn-sm.active:before {
+ opacity: 0.5;
+}
+
+.btn-toggle.btn-sm.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-sm.btn-sm:before,
+.btn-toggle.btn-sm.btn-sm:after {
+ line-height: -0.5rem;
+ color: #fff;
+ letter-spacing: 0.75px;
+ left: 0.4125rem;
+ width: 2.325rem;
+}
+
+.btn-toggle.btn-sm.btn-sm:before {
+ text-align: right;
+}
+
+.btn-toggle.btn-sm.btn-sm:after {
+ text-align: left;
+ opacity: 0;
+}
+
+.btn-toggle.btn-sm.btn-sm.active:before {
+ opacity: 0;
+}
+
+.btn-toggle.btn-sm.btn-sm.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-sm.btn-xs:before,
+.btn-toggle.btn-sm.btn-xs:after {
+ display: none;
+}
+
+.btn-toggle.btn-xs {
+ margin: 0 0;
+ padding: 0;
+ position: relative;
+ border: none;
+ height: 1rem;
+ width: 2rem;
+ border-radius: 1rem;
+}
+
+.btn-toggle.btn-xs:focus,
+.btn-toggle.btn-xs.focus,
+.btn-toggle.btn-xs:focus.active,
+.btn-toggle.btn-xs.focus.active {
+ outline: none;
+}
+
+.btn-toggle.btn-xs:before,
+.btn-toggle.btn-xs:after {
+ line-height: 1rem;
+ width: 0;
+ text-align: center;
+ font-weight: 600;
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ position: absolute;
+ bottom: 0;
+ transition: opacity 0.25s;
+}
+
+.btn-toggle.btn-xs:before {
+ content: 'Off';
+ left: 0;
+}
+
+.btn-toggle.btn-xs:after {
+ content: 'On';
+ right: 0;
+ opacity: 0.5;
+}
+
+.btn-toggle.btn-xs>.handle {
+ position: absolute;
+ top: 0.125rem;
+ left: 0.125rem;
+ width: 0.75rem;
+ height: 0.75rem;
+ border-radius: 0.75rem;
+ background: #fff;
+ transition: left 0.25s;
+}
+
+.btn-toggle.btn-xs.active {
+ transition: background-color 0.25s;
+}
+
+.btn-toggle.btn-xs.active>.handle {
+ left: 1.125rem;
+ transition: left 0.25s;
+}
+
+.btn-toggle.btn-xs.active:before {
+ opacity: 0.5;
+}
+
+.btn-toggle.btn-xs.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-xs.btn-sm:before,
+.btn-toggle.btn-xs.btn-sm:after {
+ line-height: -1rem;
+ color: #fff;
+ letter-spacing: 0.75px;
+ left: 0.275rem;
+ width: 1.55rem;
+}
+
+.btn-toggle.btn-xs.btn-sm:before {
+ text-align: right;
+}
+
+.btn-toggle.btn-xs.btn-sm:after {
+ text-align: left;
+ opacity: 0;
+}
+
+.btn-toggle.btn-xs.btn-sm.active:before {
+ opacity: 0;
+}
+
+.btn-toggle.btn-xs.btn-sm.active:after {
+ opacity: 1;
+}
+
+.btn-toggle.btn-xs.btn-xs:before,
+.btn-toggle.btn-xs.btn-xs:after {
+ display: none;
+}
+
+.btn-toggle.btn-info {
+ color: var(--white);
+ background: var(--gray);
+}
+
+.btn-toggle.btn-info:before,
+.btn-toggle.btn-info:after {
+ color: #6b7381;
+}
+
+.btn-toggle.btn-info.active {
+ background-color: var(--info);
+}
+
+.btn-toggle.btn-secondary {
+ color: #6b7381;
+ background: #bdc1c8;
+}
+
+.btn-toggle.btn-secondary:before,
+.btn-toggle.btn-secondary:after {
+ color: #6b7381;
+}
+
+.btn-toggle.btn-secondary.active {
+ background-color: #ff8300;
+}
+
+/**************************************************************/
\ No newline at end of file
diff --git a/app/frontend/static/assets/css/crafty.css b/app/frontend/static/assets/css/crafty.css
index 6c29d4ed..cc1a8b82 100644
--- a/app/frontend/static/assets/css/crafty.css
+++ b/app/frontend/static/assets/css/crafty.css
@@ -182,11 +182,55 @@ div>.input-group>.form-control {
}
.no-scroll {
- -ms-overflow-style: none; /* IE and Edge */
- scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none;
+ /* IE and Edge */
+ scrollbar-width: none;
+ /* Firefox */
}
.no-scroll::-webkit-scrollbar {
display: none;
- }
+}
+
+.custom-control-label::before,
+.custom-control-label::after {
+ cursor: pointer;
+}
+
+.custom-control-input:checked~.custom-control-label::before {
+ color: black !important;
+ background-color: blueviolet !important;
+ border-color: var(--outline) !important;
+}
+
+.custom-control-label::before {
+ background-color: white !important;
+ top: calc(-0.2rem);
+}
+
+.custom-switch .custom-control-label::after {
+ top: calc(-0.125rem + 1px);
+}
+
+/**************************************************************/
+
+/**************************************************************/
+/* CSS for Tables Displays */
+/**************************************************************/
+td>ul {
+ margin: auto;
+}
+
+td p {
+ margin: auto;
+}
+
+td.action {
+ white-space: normal;
+}
+
+td.action .btn {
+ margin-bottom: 0.2rem;
+}
+
/**************************************************************/
\ No newline at end of file
diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html
index 9795da3a..40ee757b 100755
--- a/app/frontend/templates/base.html
+++ b/app/frontend/templates/base.html
@@ -17,6 +17,7 @@
+
diff --git a/app/frontend/templates/notify.html b/app/frontend/templates/notify.html
index 4527d46b..83e80909 100644
--- a/app/frontend/templates/notify.html
+++ b/app/frontend/templates/notify.html
@@ -93,7 +93,7 @@
return true;
}
function updateAnnouncements(data) {
- console.log(data)
+ console.log(data);
let text = "";
for (let value of data) {
text += `
${value.date}
${value.desc}
Trouble Getting Annoucements
{{ translate('webhooks', 'name', data['lang']) }} - | -{{ translate('webhooks', 'type', data['lang']) }} | -{{ translate('webhooks', 'trigger', data['lang']) }} | -{{ translate('webhooks', 'enabled', - data['lang']) }} | -{{ translate('webhooks', 'edit', data['lang']) - }} | -
---|---|---|---|---|
- {{webhook.name}} - |
-
- {{webhook.webhook_type}} - |
-
-
|
- - - | -
-
- - - - - |
-
Name - | -{{ translate('webhooks', 'enabled', - data['lang']) }} | -{{ translate('webhooks', 'edit', data['lang']) - }} | -
---|---|---|
- {{webhook.name}} - |
- - - | -
-
- - - - - |
-
{{ translate('webhooks', 'enabled', data['lang']) }} | +{{ translate('webhooks', 'name', data['lang']) }} | +{{ translate('webhooks', 'type', data['lang']) }} | +{{ translate('webhooks', 'trigger', data['lang']) }} | +{{ translate('webhooks', 'edit', data['lang']) }} | +
---|---|---|---|---|
+ + | +
+ {{webhook.name}} + |
+
+ {{webhook.webhook_type}} + |
+
+
|
+ + + + + | +
{{ translate('webhooks', 'enabled', + data['lang']) }} | +Name + | +{{ translate('webhooks', 'edit', data['lang']) + }} | +
---|---|---|
+ + | +
+ {{webhook.name}} + |
+ + + + + | +