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.title}

    ${value.date}

    ${value.desc}

  • ` @@ -136,10 +136,14 @@ }); let responseData = await res.json(); console.log(responseData); + setTimeout(function() { + getAnnouncements(); + }, 1800000); //Wait 30 minutes in miliseconds + console.log("Registered annoucement fetch event in 30 minutes.") if (responseData.status === "ok") { updateAnnouncements(responseData.data) } else { - updateAnnouncements("
  • Trouble Getting Annoucements

  • ") + updateAnnouncements([]) } } async function send_clear(uuid) { diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index d3934ad7..f5b18f05 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -615,7 +615,7 @@ } - function warn(message, link = null, className = null) { + function warnServer(message, link = null, className = null) { var closeEl = document.createElement('span'); var strongEL = document.createElement('strong'); var msgEl = document.createElement('div'); @@ -799,7 +799,7 @@ setTimeout(finishTimeout, 60000); }); function finishTimeout() { - warn("It seems this is taking a while...it's possible you're using UBlock or a similar ad blocker and it's causing some of our connections to not make it to the server. Try disabling your ad blocker.", + warnServer("It seems this is taking a while...it's possible you're using UBlock or a similar ad blocker and it's causing some of our connections to not make it to the server. Try disabling your ad blocker.", null, 'wssError'); } $(".stop_button").click(function () { diff --git a/app/frontend/templates/panel/panel_edit_user.html b/app/frontend/templates/panel/panel_edit_user.html index 85a5ea46..87631219 100644 --- a/app/frontend/templates/panel/panel_edit_user.html +++ b/app/frontend/templates/panel/panel_edit_user.html @@ -363,6 +363,7 @@ data['lang']) }}{% end %} {% block js %} -{% end %} \ No newline at end of file +{% end %} diff --git a/app/frontend/templates/panel/server_webhooks.html b/app/frontend/templates/panel/server_webhooks.html index 35d09c8b..25a61257 100644 --- a/app/frontend/templates/panel/server_webhooks.html +++ b/app/frontend/templates/panel/server_webhooks.html @@ -46,104 +46,107 @@

    {{ translate('webhooks', 'webhooks', data['lang']) }}

    {% if data['user_data']['hints'] %} - + {% end %} -
    +
    {{ translate('webhooks', 'new', data['lang']) }}
    - - - - - - - - - - - - {% for webhook in data['webhooks'] %} - - - - - - - - {% end %} - -
    {{ 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}}

    -
    -
      - {% for trigger in webhook.trigger.split(",") %} - {% if trigger in data["triggers"] %} -
    • {{translate('webhooks', trigger , data['lang'])}}
    • - {%end%} - {%end%} -
    -
    - - - -
    -
    - - -
    - - - - - - - - - - {% for webhook in data['webhooks'] %} - - - - - - {% end %} - -
    Name - {{ translate('webhooks', 'enabled', - data['lang']) }}{{ translate('webhooks', 'edit', data['lang']) - }}
    -

    {{webhook.name}}

    -
    - - - -
    -
    - - -
    + {% if len(data['webhooks']) == 0 %} +
    + {{ translate('webhooks', 'no-webhook', data['lang']) }} {{ translate('webhooks', 'newWebhook',data['lang']) }}. +
    + {% end %} + {% if len(data['webhooks']) > 0 %} +
    + + + + + + + + + + + + {% for webhook in data['webhooks'] %} + + + + + + + + {% end %} + +
    {{ 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}}

    +
    +
      + {% for trigger in webhook.trigger.split(",") %} + {% if trigger in data["triggers"] %} +
    • {{translate('webhooks', trigger , data['lang'])}}
    • + {%end%} + {%end%} +
    +
    + + + +
    +
    +
    + + + + + + + + + + {% for webhook in data['webhooks'] %} + + + + + + {% end %} + +
    {{ translate('webhooks', 'enabled', + data['lang']) }}Name + {{ translate('webhooks', 'edit', data['lang']) + }}
    + + +

    {{webhook.name}}

    +
    + + + +
    +
    + {% end %}
    @@ -170,6 +173,16 @@ .toggle { height: 0px !important; } + + td.enabled { + vertical-align: middle; + border-collapse: collapse !important; + } + + .enabled .toggle-group .btn { + vertical-align: middle; + cursor: pointer; + } @@ -195,16 +208,12 @@ {% block js %}