diff --git a/install/package.json b/install/package.json index 9db6a5705d..1fcd562522 100644 --- a/install/package.json +++ b/install/package.json @@ -29,6 +29,8 @@ }, "dependencies": { "@adactive/bootstrap-tagsinput": "0.8.2", + "@fontsource/inter": "^4.5.14", + "@fontsource/poppins": "^4.5.10", "@isaacs/ttlcache": "1.4.0", "@popperjs/core": "2.11.8", "ace-builds": "1.22.0", @@ -82,7 +84,6 @@ "lodash": "4.17.21", "logrotate-stream": "0.2.9", "lru-cache": "9.1.1", - "material-design-lite": "1.3.0", "mime": "3.0.0", "mkdirp": "3.0.1", "mongodb": "5.5.0", diff --git a/public/language/en-GB/admin/admin.json b/public/language/en-GB/admin/admin.json index dcf9696ca2..89e08402cd 100644 --- a/public/language/en-GB/admin/admin.json +++ b/public/language/en-GB/admin/admin.json @@ -4,6 +4,13 @@ "acp-title": "%1 | NodeBB Admin Control Panel", "settings-header-contents": "Contents", + "changes-saved": "Changes Saved", + "changes-saved-message": "Your changes to the NodeBB configuration have been saved.", "changes-not-saved": "Changes Not Saved", - "changes-not-saved-message": "NodeBB encountered a problem saving your changes. (%1)" + "changes-not-saved-message": "NodeBB encountered a problem saving your changes. (%1)", + "save-changes": "Save changes", + "min": "Min:", + "max": "Max:", + "view": "View", + "edit": "Edit" } \ No newline at end of file diff --git a/public/language/en-GB/admin/advanced/database.json b/public/language/en-GB/admin/advanced/database.json index 9167b381ed..55eea6c023 100644 --- a/public/language/en-GB/admin/advanced/database.json +++ b/public/language/en-GB/admin/advanced/database.json @@ -5,7 +5,7 @@ "uptime-seconds": "Uptime in Seconds", "uptime-days": "Uptime in Days", - "mongo": "Mongo", + "mongo": "MongoDB", "mongo.version": "MongoDB Version", "mongo.storage-engine": "Storage Engine", "mongo.collections": "Collections", diff --git a/public/language/en-GB/admin/dashboard.json b/public/language/en-GB/admin/dashboard.json index ba945e281a..c77f7ff0c2 100644 --- a/public/language/en-GB/admin/dashboard.json +++ b/public/language/en-GB/admin/dashboard.json @@ -26,13 +26,13 @@ "updates": "Updates", "running-version": "You are running NodeBB v%1.", "keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.", - "up-to-date": "

You are up-to-date

", - "upgrade-available": "

A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-upgrade-available": "

This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.

", - "prerelease-warning": "

This is a pre-release version of NodeBB. Unintended bugs may occur.

", + "up-to-date": "You are up-to-date ", + "upgrade-available": "A new version (v%1) has been released. Consider upgrading your NodeBB.", + "prerelease-upgrade-available": "This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.", + "prerelease-warning": "This is a pre-release version of NodeBB. Unintended bugs may occur. ", "fallback-emailer-not-found": "Fallback emailer not found!", - "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.", - "latest-lookup-failed": "

Failed to look up latest available version of NodeBB

", + "running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator", + "latest-lookup-failed": "Failed to look up latest available version of NodeBB", "notices": "Notices", "restart-not-required": "Restart not required", diff --git a/public/language/en-GB/admin/extend/plugins.json b/public/language/en-GB/admin/extend/plugins.json index f7e60c4360..4849f0cab2 100644 --- a/public/language/en-GB/admin/extend/plugins.json +++ b/public/language/en-GB/admin/extend/plugins.json @@ -1,4 +1,5 @@ { + "plugins": "Plugins", "trending": "Trending", "installed": "Installed", "active": "Active", diff --git a/public/language/en-GB/admin/extend/rewards.json b/public/language/en-GB/admin/extend/rewards.json index df89d441a7..2706a25f51 100644 --- a/public/language/en-GB/admin/extend/rewards.json +++ b/public/language/en-GB/admin/extend/rewards.json @@ -1,10 +1,12 @@ { "rewards": "Rewards", + "add-reward": "Add reward", "condition-if-users": "If User's", "condition-is": "Is:", "condition-then": "Then:", "max-claims": "Amount of times reward is claimable", "zero-infinite": "Enter 0 for infinite", + "select-reward": "Select reward", "delete": "Delete", "enable": "Enable", "disable": "Disable", diff --git a/public/language/en-GB/admin/extend/widgets.json b/public/language/en-GB/admin/extend/widgets.json index 9adfb98ab5..ab3a4b11d2 100644 --- a/public/language/en-GB/admin/extend/widgets.json +++ b/public/language/en-GB/admin/extend/widgets.json @@ -1,4 +1,5 @@ { + "widgets": "Widgets", "available": "Available Widgets", "explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.", "none-installed": "No widgets found! Activate the widget essentials plugin in the plugins control panel.", diff --git a/public/language/en-GB/admin/manage/admins-mods.json b/public/language/en-GB/admin/manage/admins-mods.json index f9bbc63632..999ce33aa4 100644 --- a/public/language/en-GB/admin/manage/admins-mods.json +++ b/public/language/en-GB/admin/manage/admins-mods.json @@ -1,10 +1,11 @@ { + "manage-admins-and-mods": "Manage Admins & Mods", "administrators": "Administrators", "global-moderators": "Global Moderators", "moderators": "Moderators", "no-global-moderators": "No Global Moderators", "no-sub-categories": "No subcategories", - "subcategories": "%1 subcategories", + "view-children": "View children (%1)", "no-moderators": "No Moderators", "add-administrator": "Add Administrator", "add-global-moderator": "Add Global Moderator", diff --git a/public/language/en-GB/admin/manage/categories.json b/public/language/en-GB/admin/manage/categories.json index ed5462e9be..0cfb251c56 100644 --- a/public/language/en-GB/admin/manage/categories.json +++ b/public/language/en-GB/admin/manage/categories.json @@ -1,7 +1,11 @@ { + "manage-categories": "Manage Categories", + "add-category": "Add category", + "jump-to": "Jump to...", "settings": "Category Settings", + "edit-category": "Edit Category", "privileges": "Privileges", - + "back-to-categories": "Back to categories", "name": "Category Name", "description": "Category Description", "bg-color": "Background Colour", @@ -15,8 +19,11 @@ "post-queue": "Post queue", "tag-whitelist": "Tag Whitelist", "upload-image": "Upload Image", + "upload": "Upload", + "select-icon": "Select Icon", "delete-image": "Remove", "category-image": "Category Image", + "image-and-icon": "Image & Icon", "parent-category": "Parent Category", "optional-parent-category": "(Optional) Parent Category", "top-level": "Top Level", @@ -31,6 +38,7 @@ "disable": "Disable", "edit": "Edit", "analytics": "Analytics", + "view-category": "View category", "set-order": "Set order", "set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.", diff --git a/public/language/en-GB/admin/manage/groups.json b/public/language/en-GB/admin/manage/groups.json index bd8c5bce15..9b98b745fa 100644 --- a/public/language/en-GB/admin/manage/groups.json +++ b/public/language/en-GB/admin/manage/groups.json @@ -1,4 +1,10 @@ { + "manage-groups": "Manage groups", + "add-group": "Add group", + "edit-group": "Edit Group", + "back-to-groups": "Back to groups", + "view-group": "View group", + "icon-and-title": "Icon & Title", "name": "Group Name", "badge": "Badge", "properties": "Properties", @@ -10,7 +16,7 @@ "edit": "Edit", "delete": "Delete", "privileges": "Privileges", - "download-csv": "CSV", + "members-csv": "Members (CSV)", "search-placeholder": "Search", "create": "Create Group", "description-placeholder": "A short description about your group", diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json index 0e8d7a4ff9..9af075bcfa 100644 --- a/public/language/en-GB/admin/manage/privileges.json +++ b/public/language/en-GB/admin/manage/privileges.json @@ -1,4 +1,6 @@ { + "manage-privileges": "Manage Privileges", + "discard-changes": "Discard changes", "global": "Global", "admin": "Admin", "group-privileges": "Group Privileges", diff --git a/public/language/en-GB/admin/manage/tags.json b/public/language/en-GB/admin/manage/tags.json index 01363dfda0..2bb50e7048 100644 --- a/public/language/en-GB/admin/manage/tags.json +++ b/public/language/en-GB/admin/manage/tags.json @@ -1,9 +1,11 @@ { + "manage-tags": "Manage Tags", "none": "Your forum does not have any topics with tags yet.", "bg-color": "Background Colour", "text-color": "Text Colour", "description": "Select tags by clicking or dragging, use CTRL to select multiple tags.", "create": "Create Tag", + "add-tag": "Add tag", "modify": "Modify Tags", "rename": "Rename Tags", "delete": "Delete Selected Tags", diff --git a/public/language/en-GB/admin/manage/uploads.json b/public/language/en-GB/admin/manage/uploads.json index 72a695ccdc..e5f3a2a940 100644 --- a/public/language/en-GB/admin/manage/uploads.json +++ b/public/language/en-GB/admin/manage/uploads.json @@ -1,4 +1,5 @@ { + "manage-uploads": "Manage Uploads", "upload-file": "Upload File", "filename": "Filename", "usage": "Post Usage", diff --git a/public/language/en-GB/admin/manage/users.json b/public/language/en-GB/admin/manage/users.json index 9064153de7..6b668a31ef 100644 --- a/public/language/en-GB/admin/manage/users.json +++ b/public/language/en-GB/admin/manage/users.json @@ -1,4 +1,5 @@ { + "manage-users": "Manage Users", "users": "Users", "edit": "Actions", "make-admin": "Make Admin", diff --git a/public/language/en-GB/admin/menu.json b/public/language/en-GB/admin/menu.json index 379e0b2687..bdc794d943 100644 --- a/public/language/en-GB/admin/menu.json +++ b/public/language/en-GB/admin/menu.json @@ -72,7 +72,9 @@ "development/info": "Info", "rebuild-and-restart-forum": "Rebuild & Restart Forum", + "rebuild-and-restart": "Rebuild & Restart", "restart-forum": "Restart Forum", + "restart": "Restart", "logout": "Log out", "view-forum": "View Forum", diff --git a/public/language/en-GB/admin/settings/general.json b/public/language/en-GB/admin/settings/general.json index 29b939861b..2369a49d1c 100644 --- a/public/language/en-GB/admin/settings/general.json +++ b/public/language/en-GB/admin/settings/general.json @@ -1,11 +1,13 @@ { + "general-settings": "General Settings", + "on-this-page": "On this page:", "site-settings": "Site Settings", "title": "Site Title", "title.short": "Short Title", "title.short-placeholder": "If no short title is specified, the site title will be used", "title.url": "Title Link URL", "title.url-placeholder": "The URL of the site title", - "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index.
Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", + "title.url-help": "When the title is clicked, send users to this address. If left blank, user will be sent to the forum index. Note: This is not the external URL used in emails, etc. That is set by the url property in config.json", "title.name": "Your Community Name", "title.show-in-header": "Show Site Title in Header", "browser-title": "Browser Title", @@ -16,7 +18,7 @@ "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", - "logo": "Site Logo", + "logo-and-icons": "Site Logo & Icons", "logo.image": "Image", "logo.image-placeholder": "Path to a logo to display on forum header", "logo.upload": "Upload", diff --git a/public/language/en-GB/admin/settings/guest.json b/public/language/en-GB/admin/settings/guest.json index 75d44f37e4..44370e3668 100644 --- a/public/language/en-GB/admin/settings/guest.json +++ b/public/language/en-GB/admin/settings/guest.json @@ -1,5 +1,6 @@ { "settings": "Settings", + "guest-settings": "Guest settings", "handles.enabled": "Allow guest handles", "handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\"", "topic-views.enabled": "Allow guests to increase topic view counts", diff --git a/public/language/en-GB/admin/settings/navigation.json b/public/language/en-GB/admin/settings/navigation.json index 32b2722074..931ac5f4ba 100644 --- a/public/language/en-GB/admin/settings/navigation.json +++ b/public/language/en-GB/admin/settings/navigation.json @@ -1,4 +1,5 @@ { + "navigation": "Navigation", "icon": "Icon:", "change-icon": "change", "route": "Route:", diff --git a/public/language/en-GB/admin/settings/post.json b/public/language/en-GB/admin/settings/post.json index 57cc855319..9dc61afe57 100644 --- a/public/language/en-GB/admin/settings/post.json +++ b/public/language/en-GB/admin/settings/post.json @@ -1,4 +1,5 @@ { + "general": "General", "sorting": "Post Sorting", "sorting.post-default": "Default Post Sorting", "sorting.oldest-to-newest": "Oldest to Newest", @@ -23,10 +24,8 @@ "restrictions.seconds-edit-after": "Number of seconds a post remains editable (set to 0 to disable)", "restrictions.seconds-delete-after": "Number of seconds a post remains deletable (set to 0 to disable)", "restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics (set to 0 to disable)", - "restrictions.min-title-length": "Minimum Title Length", - "restrictions.max-title-length": "Maximum Title Length", - "restrictions.min-post-length": "Minimum Post Length", - "restrictions.max-post-length": "Maximum Post Length", + "restrictions.title-length": "Title Length", + "restrictions.post-length": "Post Length", "restrictions.days-until-stale": "Days until topic is considered stale", "restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic.", "timestamp": "Timestamp", @@ -41,10 +40,9 @@ "teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies", "teaser.first": "First", "showPostPreviewsOnHover": "Show a preview of posts when mouse overed", - "unread": "Unread Settings", + "unread-and-recent": "Unread & Recent Settings", "unread.cutoff": "Unread cutoff days", "unread.min-track-last": "Minimum posts in topic before tracking last read", - "recent": "Recent Settings", "recent.max-topics": "Maximum topics on /recent", "recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page", "signature": "Signature Settings", diff --git a/public/language/en-GB/admin/settings/reputation.json b/public/language/en-GB/admin/settings/reputation.json index e790ec094f..293aa5b440 100644 --- a/public/language/en-GB/admin/settings/reputation.json +++ b/public/language/en-GB/admin/settings/reputation.json @@ -27,5 +27,5 @@ "flags.action-on-resolve": "Do the following when a flag is resolved", "flags.action-on-reject": "Do the following when a flag is rejected", "flags.action.nothing": "Do nothing", - "flags.action.rescind": "Rescind the notification send to moderators/administrators" + "flags.action.rescind": "Rescind the notification sent to moderators/administrators" } \ No newline at end of file diff --git a/public/language/en-GB/admin/settings/social.json b/public/language/en-GB/admin/settings/social.json index 23aedfcfaa..257e20b54b 100644 --- a/public/language/en-GB/admin/settings/social.json +++ b/public/language/en-GB/admin/settings/social.json @@ -1,5 +1,4 @@ { "post-sharing": "Post Sharing", - "info-plugins-additional": "Plugins can add additional networks for sharing posts.", - "save-success": "Successfully saved Post Sharing Networks!" + "info-plugins-additional": "Plugins can add additional networks for sharing posts." } \ No newline at end of file diff --git a/public/language/en-GB/admin/settings/tags.json b/public/language/en-GB/admin/settings/tags.json index 080010f6f0..c1cdb2b644 100644 --- a/public/language/en-GB/admin/settings/tags.json +++ b/public/language/en-GB/admin/settings/tags.json @@ -3,6 +3,7 @@ "link-to-manage": "Manage Tags", "system-tags": "System Tags", "system-tags-help": "Only privileged users will be able to use these tags.", + "tags-per-topic": "Tags per topic", "min-per-topic": "Minimum Tags per Topic", "max-per-topic": "Maximum Tags per Topic", "min-length": "Minimum Tag Length", diff --git a/public/language/en-GB/admin/settings/user.json b/public/language/en-GB/admin/settings/user.json index c8c418a206..368548ba74 100644 --- a/public/language/en-GB/admin/settings/user.json +++ b/public/language/en-GB/admin/settings/user.json @@ -59,7 +59,7 @@ "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", - "user-search-results-per-page": "Number of results to display", + "user-search-results-per-page": "Number of users to display in search results", "default-user-settings": "Default User Settings", "show-email": "Show email", "show-fullname": "Show fullname", diff --git a/public/language/en-GB/global.json b/public/language/en-GB/global.json index 09a90dea4c..857bd05377 100644 --- a/public/language/en-GB/global.json +++ b/public/language/en-GB/global.json @@ -24,6 +24,7 @@ "save_changes": "Save Changes", "save": "Save", + "create": "Create", "cancel": "Cancel", "close": "Close", diff --git a/public/openapi/read.yaml b/public/openapi/read.yaml index 5343d130ae..34326163bd 100644 --- a/public/openapi/read.yaml +++ b/public/openapi/read.yaml @@ -82,14 +82,8 @@ paths: $ref: 'read/admin/dashboard/searches.yaml' "/api/admin/settings/{term}": $ref: 'read/admin/settings/term.yaml' - /api/admin/settings/languages: - $ref: 'read/admin/settings/languages.yaml' /api/admin/settings/navigation: $ref: 'read/admin/settings/navigation.yaml' - /api/admin/settings/homepage: - $ref: 'read/admin/settings/homepage.yaml' - /api/admin/settings/social: - $ref: 'read/admin/settings/social.yaml' /api/admin/settings/api: $ref: 'read/admin/settings/api.yaml' /api/admin/settings/email: diff --git a/public/openapi/read/admin/manage/categories.yaml b/public/openapi/read/admin/manage/categories.yaml index 36b8d4f9a8..b4e6102ac1 100644 --- a/public/openapi/read/admin/manage/categories.yaml +++ b/public/openapi/read/admin/manage/categories.yaml @@ -13,6 +13,8 @@ get: properties: categoriesPerPage: type: number + selectCategoryLabel: + type: string categoriesTree: type: array items: @@ -23,6 +25,8 @@ get: description: A category identifier name: type: string + description: + type: string disabled: type: number icon: diff --git a/public/openapi/read/admin/settings/advanced.yaml b/public/openapi/read/admin/settings/advanced.yaml index 2cebeeb7e9..c6dfbbbcd9 100644 --- a/public/openapi/read/admin/settings/advanced.yaml +++ b/public/openapi/read/admin/settings/advanced.yaml @@ -11,6 +11,8 @@ get: allOf: - type: object properties: + title: + type: string groupsExemptFromMaintenanceMode: type: array items: diff --git a/public/openapi/read/admin/settings/api.yaml b/public/openapi/read/admin/settings/api.yaml index 1e955d32c8..29fde9f691 100644 --- a/public/openapi/read/admin/settings/api.yaml +++ b/public/openapi/read/admin/settings/api.yaml @@ -11,6 +11,8 @@ get: allOf: - type: object properties: + title: + type: string tokens: type: array items: diff --git a/public/openapi/read/admin/settings/email.yaml b/public/openapi/read/admin/settings/email.yaml index e2d9b76257..235d6cc833 100644 --- a/public/openapi/read/admin/settings/email.yaml +++ b/public/openapi/read/admin/settings/email.yaml @@ -11,6 +11,8 @@ get: allOf: - type: object properties: + title: + type: string emails: type: array items: diff --git a/public/openapi/read/admin/settings/homepage.yaml b/public/openapi/read/admin/settings/homepage.yaml deleted file mode 100644 index 3225629c37..0000000000 --- a/public/openapi/read/admin/settings/homepage.yaml +++ /dev/null @@ -1,23 +0,0 @@ -get: - tags: - - admin - summary: Get homepage settings - responses: - "200": - description: "" - content: - application/json: - schema: - allOf: - - type: object - properties: - routes: - type: array - items: - type: object - properties: - route: - type: string - name: - type: string - - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/admin/settings/languages.yaml b/public/openapi/read/admin/settings/languages.yaml deleted file mode 100644 index 64b1e6b54e..0000000000 --- a/public/openapi/read/admin/settings/languages.yaml +++ /dev/null @@ -1,35 +0,0 @@ -get: - tags: - - admin - summary: Get language settings - responses: - "200": - description: A JSON object containing available languages and settings - content: - application/json: - schema: - allOf: - - type: object - properties: - languages: - type: array - items: - type: object - properties: - name: - type: string - description: Localised name of the language - code: - type: string - description: A language code (similar to ISO-639) - dir: - type: string - description: Directionality of the language - enum: [ltr, rtl] - selected: - type: boolean - description: Denotes the currently selected default system language on the forum - autoDetectLang: - type: integer - description: Whether the forum will attempt to guess language based on browser's `Accept-Language` header - - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/admin/settings/navigation.yaml b/public/openapi/read/admin/settings/navigation.yaml index 05ffbc6bf7..c86f387997 100644 --- a/public/openapi/read/admin/settings/navigation.yaml +++ b/public/openapi/read/admin/settings/navigation.yaml @@ -111,4 +111,6 @@ get: navigation: type: array description: A clone of `enabled` + title: + type: string - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/admin/settings/post.yaml b/public/openapi/read/admin/settings/post.yaml index f8273c8b8c..9fdf7685c4 100644 --- a/public/openapi/read/admin/settings/post.yaml +++ b/public/openapi/read/admin/settings/post.yaml @@ -11,6 +11,8 @@ get: allOf: - type: object properties: + title: + type: string groupsExemptFromPostQueue: type: array items: diff --git a/public/openapi/read/admin/settings/social.yaml b/public/openapi/read/admin/settings/social.yaml deleted file mode 100644 index 7d32a17064..0000000000 --- a/public/openapi/read/admin/settings/social.yaml +++ /dev/null @@ -1,28 +0,0 @@ -get: - tags: - - admin - summary: Get post social sharing settings - responses: - "200": - description: "A JSON object containing post social sharing settings" - content: - application/json: - schema: - allOf: - - type: object - properties: - posts: - type: array - items: - type: object - properties: - id: - type: string - name: - type: string - class: - type: string - description: A FontAwesome icon string - activated: - type: boolean - - $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file diff --git a/public/openapi/read/admin/settings/term.yaml b/public/openapi/read/admin/settings/term.yaml index 1801041776..86eaf50b4e 100644 --- a/public/openapi/read/admin/settings/term.yaml +++ b/public/openapi/read/admin/settings/term.yaml @@ -17,7 +17,17 @@ get: schema: allOf: - type: object - properties: {} + properties: + title: + type: string + routes: + type: array + postSharing: + type: array + languages: + type: array + autoDetectLang: + type: number additionalProperties: type: object description: Most of the settings pages have their values loaded on the client-side, so the settings are not exposed server-side. diff --git a/public/openapi/read/admin/settings/user.yaml b/public/openapi/read/admin/settings/user.yaml index 6dccccedee..89b0b1067a 100644 --- a/public/openapi/read/admin/settings/user.yaml +++ b/public/openapi/read/admin/settings/user.yaml @@ -11,6 +11,8 @@ get: allOf: - type: object properties: + title: + type: string notificationSettings: type: array items: diff --git a/public/scss/admin/admin.scss b/public/scss/admin/admin.scss index 1be7f930f5..c7ee741967 100644 --- a/public/scss/admin/admin.scss +++ b/public/scss/admin/admin.scss @@ -1,6 +1,8 @@ -@import "./mixins"; +@import "fonts"; +@import "mixins"; +@import "common"; -@import "./header"; +@import "sidebar"; @import "./mobile"; @import "./general/dashboard"; @@ -12,10 +14,8 @@ @import "./settings/api"; @import "./appearance/customise"; @import "./extend/plugins"; -@import "./extend/rewards"; @import "./extend/widgets"; -@import "./advanced/database"; -@import "./settings"; +@import "settings"; @import "./modules/alerts"; @import "./modules/selectable"; @@ -27,26 +27,29 @@ body { } .admin { - background: #fff; - font-size: 14px; - - h1 { - font-size: 35px; - } - .form-label, .form-check-label { - font-weight: 700; - } - .btn { - border-radius: 0; - } - .btn-outline-secondary { - color: $gray-700; - box-shadow: 0 1px 4px rgba(0, 0, 0, .4) !important; - &:hover { - background-color: $gray-200 !important; + .acp-page-container { + max-width: 800px; + margin: 0 auto; + display: flex; + flex-direction: column; + gap: $spacer * 1.5; + padding: $spacer * 1.5; + padding-top: 0; + } + .acp-page-main-header { + background-color: white; + } + + .settings, .categories, .category, .admins-mods { + hr { + color: $gray-500; } } + .form-control::placeholder, .bootstrap-tagsinput::placeholder { + color: $gray-500 !important; + } + // .floating-button can either be a container or the button itself .floating-button { position: fixed; @@ -73,22 +76,6 @@ body { background: $primary !important; } - .nodebb-logo { - img { - height: 31px; - margin-top: -8px; - margin-left: -7px; - vertical-align: -43%; - } - - @include box-header-font; - color: #fff; - } - - #breadcrumbs { - cursor: default; - } - @mixin acp-panel-heading() { padding: 7px 14px; border: 0; @@ -114,10 +101,6 @@ body { } } - .nav-header { - @include box-header-font; - } - .icon-container { .fa-nbb-none { border: 1px dotted black; @@ -141,27 +124,6 @@ body { } } - .navbar-static-top, .navbar-fixed-top { - box-shadow: 0px -3px 12px rgba(0, 0, 0, 0.5); - } - - .navbar-header > .navbar-toggle { - margin-right: 8px; - } - - .navbar-nav { - >li { - >#reconnect { - color: $gray-700; - } - - >#reconnect:focus, >#reconnect:hover { - color: $gray-700; - background-color: transparent; - } - } - } - #taskbar { display: none; /* not sure why I have to do this, but it only seems to show up on prod */ } @@ -172,57 +134,21 @@ body { } .bootstrap-tagsinput { + box-shadow: $input-box-shadow; width: 100%; - border: 0; - box-shadow: none; - padding-left: 0; input { - width: 100%; - margin-left: 1px; - margin-top: 9px; - border-bottom: 1px dotted #ccc !important; - padding-bottom: 5px; - padding-left: 0; + font-size: 0.875rem; + width: 64px; + padding: 0; } } } -// Allowing text to the right of an image-type brand -// See: https://github.com/twbs/bootstrap/commit/8e2348e9eda51296eb680192379ab37f10355ca3 -.navbar-brand > img { - display: inline-block; -} - -.category-settings-form { - h3 { - margin-top: 0; - cursor: pointer; - } - - h4 { - cursor: pointer; - } -} - -.category-preview { - cursor: pointer; - width: 100%; - height: 100px; - text-align: center; - color: white; - margin-top: 0; - - .icon { - width: 30px; - height: 30px; - line-height: 40px; - display: inline-block; - margin: 35px 5px 0 5px; - } +.dropdown-left .dropdown-menu { + --bs-position: start; } - -.category-dropdown-container.right .category-dropdown-menu { +.dropdown-right .dropdown-menu { --bs-position: end; } @@ -261,10 +187,6 @@ body { opacity: 0.5; } -form small { - color: $gray-700; -} - .caret { display: inline-block; width: 0; diff --git a/public/scss/admin/advanced/database.scss b/public/scss/admin/advanced/database.scss deleted file mode 100644 index a68332708c..0000000000 --- a/public/scss/admin/advanced/database.scss +++ /dev/null @@ -1,6 +0,0 @@ -.database-info { - span { - display:inline-block; - width:220px; - } -} diff --git a/public/scss/admin/common.scss b/public/scss/admin/common.scss new file mode 100644 index 0000000000..85cea66ed2 --- /dev/null +++ b/public/scss/admin/common.scss @@ -0,0 +1,73 @@ + +.form-label { + font-weight: 500; + font-size: $font-size-sm; + font-family: $font-family-base; + margin-bottom: 0; +} + +.form-text { + font-size: 0.75rem!important;; + font-family: $font-family-base; +} + +.tracking-tight { letter-spacing: -0.02em; } + +$btn-ghost-hover-color: mix($light, $dark, 90%); + +@mixin btn-ghost-base { + display: flex; + align-items: center; + justify-content: center; + gap: ($spacer * 0.5); + border-radius: $border-radius-sm; + border-width: 1px; + border-color: transparent; + background-color: transparent; + padding: ($spacer * 0.25) ($spacer * 0.5); + text-align: left; + --bs-text-opacity: 1; + color: rgb(73 80 87 / var(--bs-text-opacity)); + font-family: $font-family-secondary; + cursor: pointer; + &:hover, &.active { + background-color: $btn-ghost-hover-color; + color: rgb(73 80 87 / var(--bs-text-opacity)); + text-decoration: none; + } +} + +.btn-ghost { + @include btn-ghost-base(); + line-height: 1.5rem; + > i { + line-height: 1.5rem; + } +} + +.btn-ghost-sm { + @include btn-ghost-base(); + font-size: 0.875rem; + line-height: 1.25rem; + > i { + line-height: 1.25rem; + } +} + +.btn-outline { + @include btn-ghost-base(); + border-color: $border-color; +} + +.btn-outline-sm { + @include btn-ghost-base(); + border-color: $border-color; + font-size: 0.875rem; + line-height: 1.25rem; +} + +.flex-basis-md-200 { + @include media-breakpoint-up(md) { + flex-basis: 200px!important; + } +} \ No newline at end of file diff --git a/public/scss/admin/extend/plugins.scss b/public/scss/admin/extend/plugins.scss index 70c441c190..0dcd19b2d2 100644 --- a/public/scss/admin/extend/plugins.scss +++ b/public/scss/admin/extend/plugins.scss @@ -1,24 +1,4 @@ .plugins { - padding-left: 0px; - - li { - list-style-type: none; - background: rgba(64, 64, 64, 0.05); - padding: 1em; - margin-bottom: 5px; - border-left: 5px solid #08c; - margin-left: -40px; - - h2 { - font-size: 16px; - margin: 0; - } - - p { - font-size: 12px; - } - } - .plugin-list.ui-sortable { li { cursor: pointer; diff --git a/public/scss/admin/extend/rewards.scss b/public/scss/admin/extend/rewards.scss deleted file mode 100644 index 4f20b44bf2..0000000000 --- a/public/scss/admin/extend/rewards.scss +++ /dev/null @@ -1,52 +0,0 @@ -#rewards { - ul { - list-style-type: none; - padding: 0px; - margin: 0px; - - > li { - border-bottom: 1px solid #ddd; - margin-bottom: 20px; - &:last-child { - border-bottom: 0; - } - } - } - - .rewards { width: 100%; } - - .card { - border-radius: 2px; - border-width: 2px; - color: #333; - - &.if-block { - border-color: $primary; - max-width: 33%; - } - &.this-block { - border-color: $warning; - max-width: 33%; - } - &.then-block { - border-color: $success; - max-width: 33%; - } - &.reward-block { - border-color: $success; - background-color: lighten($success, 15%); - color: #fff; - a, select, input { color: #fff; } - select > option { color: #333; } - width: 100%; - min-height: 110px; - } - } -} - -.page-admin-rewards { - #new { - bottom: calc(64px + $spacer); - background-color: $success; - } -} \ No newline at end of file diff --git a/public/scss/admin/fonts.scss b/public/scss/admin/fonts.scss new file mode 100644 index 0000000000..11b0d4d1a2 --- /dev/null +++ b/public/scss/admin/fonts.scss @@ -0,0 +1,21 @@ +@use "@fontsource/inter/scss/mixins" as Inter; +@use "@fontsource/poppins/scss/mixins" as Poppins; + +$weights: $font-weight-light, $font-weight-normal, $font-weight-semibold, $font-weight-bold; + +@each $weight in $weights { + @include Inter.fontFace( + $weight: $weight, + $display: fallback, + $fontDir: "./plugins/core/inter" + ); + @include Poppins.fontFace( + $weight: $weight, + $display: fallback, + $fontDir: "./plugins/core/poppins" + ); +} + +.ff-base { font-family: $font-family-base !important; } +.ff-sans { font-family: $font-family-sans-serif !important; } +.ff-secondary { font-family: $font-family-secondary; } \ No newline at end of file diff --git a/public/scss/admin/header.scss b/public/scss/admin/header.scss deleted file mode 100644 index 1d9b08b4b9..0000000000 --- a/public/scss/admin/header.scss +++ /dev/null @@ -1,159 +0,0 @@ -.header { - user-select: none; - position: relative; - background: #333; - width: 100%; - height: 200px; - margin-bottom: 50px; - font-size: 16px; - - #main-page-title { - position: absolute; - left: 48px; - bottom: 50px; - color: #aaa; - font-size: 47px; - font-weight: 300; - } - - .quick-actions { - position: static; - padding: 15px; - display: flex; - flex-direction: row-reverse; - margin: 0; - - > * { - margin-right: 20px; - } - - > .menu-button { - margin-right: 0; - padding: 0 5px; - } - - .alert { - font-size: 14px; - margin-bottom: 0px; - - &.alert-info { - background-color: #eee; - color: #333; - } - } - - .dropdown { - margin-right: 0px; - - .dropdown-toggle i { - padding: 0 1rem; - } - } - - .fa { - line-height: 44px; - font-size: 25px; - } - - #user_dropdown { - font-size: 25px; - color: #eee; - - i { - margin-top: 12px; - display: block; - } - } - } - - #acp-search { - input { - padding: 10px 20px; - width: 250px; - height: 44px; - background-color: rgba(0,0,0,.2); - border-radius: 3px; - box-shadow: none; - transition: .4s ease background-color; - - &:focus { - background-color: #eee; - color: #333; - } - } - - .dropdown:not(.open) { - &:before { - content: '/'; - border: 1px solid $gray-500; - border-radius: 5px; - padding: 0px 6px; - font-size: 12px; - font-weight: 600; - pointer-events: none; - - position: absolute; - top: 13px; - left: 1em; - } - - &:after { - content: attr(data-text); - position: absolute; - top: 13px; - left: 3em; - font-size: small; - font-weight: 600; - pointer-events: none; - } - - input { - color: transparent; - } - } - - .search-match { - font-weight: 700; - color: black; - } - } - - #main-menu > li { - padding-bottom: 10px; - } - - > ul { - list-style-type: none; - padding: 0px; - position: absolute; - bottom: -16px; - left: 50px; - - > li { - float: left; - margin-right: 30px; - border-bottom: 4px solid transparent; - transition: border-color 150ms linear; - - &:hover { - border-color: darken($primary, 20%); - } - - &.active { - border-color: $primary; - } - - > a { - color: white; - text-transform: uppercase; - text-decoration: none; - outline: none; - } - } - } - - .plugins-menu { - max-height: 50vh; - overflow-y: auto; - } -} diff --git a/public/scss/admin/manage/categories.scss b/public/scss/admin/manage/categories.scss index 8c5117990f..39c61f429f 100644 --- a/public/scss/admin/manage/categories.scss +++ b/public/scss/admin/manage/categories.scss @@ -1,51 +1,20 @@ div.categories { ul[data-cid] { - user-select: none; - list-style-type: none; - margin: 0; - padding: 0; - > li > ul > li { - margin-left: 4.5rem; - } - > li > a { - margin-left: 4.5rem; - } - .row { - margin-left: -15px; - margin-right: -15px; + padding-left: 3rem; } - > li li:last-child { - .row { - border-bottom: 0px; - } - } > li { - margin: 16px 0 24px 0; - &.placeholder { border: 1px dashed #2196F3; background-color: #E1F5FE; + width: 100%; } } } - .stats { - display: inline-block; - - li { - min-height: 0; - display: inline; - margin: 0 16px 0 0; - left: 0; - } - } - - .disabled > .category-row { - - .icon, .category-header, .description { + .icon, .title, .description { opacity: 0.5; } @@ -57,34 +26,15 @@ div.categories { .toggle { width: 24px; height: 24px; - border-radius: 50%; line-height: 24px; - text-align: center; - vertical-align: bottom; - background-size: cover; - float: left; - margin-right: 0px; cursor: pointer; - .fa { - font-size: 85%; - } } .information { cursor: move; - padding-left: 3rem; - } - - .category-header { - margin-top: 0; - margin-bottom: 8px; - } - - .description { - margin: 0; } - .children-placeholder{ + .children-placeholder { min-height: 20px; height: 20px; } diff --git a/public/scss/admin/manage/groups.scss b/public/scss/admin/manage/groups.scss index 8cad8083b2..24718ac43d 100644 --- a/public/scss/admin/manage/groups.scss +++ b/public/scss/admin/manage/groups.scss @@ -1,30 +1,5 @@ -.group { - [component="groups/members"] { - padding: 0; - tbody { - max-height: 500px; - display: block; - overflow-y: auto; - .member-name { - width: 100%; - } - } - } - - #group-icon { - cursor: pointer; - } -} - .groups { - #group-search { - margin-bottom: 10px; - } - .groups-list { - p { - margin: 0; - } td { max-width: 350px; } diff --git a/public/scss/admin/manage/tags.scss b/public/scss/admin/manage/tags.scss index dc7d608129..d242288997 100644 --- a/public/scss/admin/manage/tags.scss +++ b/public/scss/admin/manage/tags.scss @@ -1,22 +1,12 @@ .tags { .tag-list { - h3 { - min-width: 225px; - } .tag-row { - padding: 0.5rem; float: left; - margin-left: 0.5rem; - - .tag-item { - cursor: pointer; - display: inline-block; - font-size: 11px; - } &.ui-selected { background: lighten($success, 25%); + border-radius: $border-radius-sm; } &.ui-selecting { @@ -24,8 +14,4 @@ } } } - - .tag-topic-count { - font-size: 14px; - } } \ No newline at end of file diff --git a/public/scss/admin/mobile.scss b/public/scss/admin/mobile.scss index d0a509e8ac..6ebca156d8 100644 --- a/public/scss/admin/mobile.scss +++ b/public/scss/admin/mobile.scss @@ -1,191 +1,12 @@ -#mobile-menu { - display: none; -} - @media (max-width: 991px) { body { height: 100%; - } - - #panel { - background-color: inherit; - min-height: 100%; - } - - body, #panel, .slideout-menu { - -webkit-overflow-scrolling: touch; - } - - .header { - height: 58px; - box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.26); - position: fixed; - top: 0px; - z-index: 5; - - #main-page-title { - bottom: -31px; - font-size: 20px; - color: #FFF; - left: 52px; - font-weight: 400; - } - - #main-menu { - display: none; - } - } - - #mobile-menu { - width: 22px; - background: none; - border: none; - margin-right: 10px; - margin-left: -5px; - outline: none !important; - display: block; - - position: absolute; - top: 22px; - left: 22px; - - .bar { - width: 100%; - height: 2px; - background: #fff; - margin-bottom: 3px; - border-radius: 10px; - } - } - - #menu { - background-color: #1D1F20; - background-image: linear-gradient(145deg, #1D1F20, #404348); - - a { - color: #fff; - text-decoration: none; - } - - a:hover { - text-decoration: underline; - } - } - - .menu-header-title { - font-weight: 400; - letter-spacing: 0.5px; - margin: 0; - } - - .menu-section { - margin: 25px 0; - - &.quick-actions { - margin: 0; - - .button-group { - display: flex; - justify-content: center; - } - - .alert { - border-radius: 0; - - .span { - display: block; - } - } - } - } - - .menu-section-title { - text-transform: uppercase; - color: #85888d; - font-weight: 200; - font-size: 13px; - letter-spacing: 1px; - padding: 0 20px; - margin:0; - } - - .menu-section-list { - padding: 0; - margin: 10px 0; - list-style: none; - - a { - display: block; - padding: 10px 20px; - } - - a:hover { - background-color: rgba(255, 255, 255, 0.1); - text-decoration: none; - } - } - - #panel { - background: white; - min-height: 100%; - padding-top: 80px; - } - - .slideout-menu { - position: fixed; - left: 0; - top: 0; - bottom: 0; - right: 0; - z-index: 0; - width: 256px; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - display: none; - } - - - .slideout-panel { - position: relative; - } - - .slideout-open, - .slideout-open body, - .slideout-open .slideout-panel { - overflow: hidden; - overflow-y: hidden !important; - } - - .slideout-open .slideout-menu { - display: block; + overflow-y: scroll; + overflow-x: hidden; } html { height: 100%; overflow-y: hidden; } - - .slideout-open { - overflow-y: hidden; - height: 100%; - } - - body { - overflow-y: scroll; - overflow-x: hidden; - } } - -@media (max-width: 768px) { - .content-header, .settings-header { - font-size: 200%; - margin-bottom: 20px; - margin-left: -2px; - } - - - .dropdown-menu { - margin-top: -35px; - margin-right: -2px; - } -} \ No newline at end of file diff --git a/public/scss/admin/modules/search.scss b/public/scss/admin/modules/search.scss index 57b8d8613e..c25748f85a 100644 --- a/public/scss/admin/modules/search.scss +++ b/public/scss/admin/modules/search.scss @@ -1,12 +1,9 @@ -#acp-search { +.acp-search { .dropdown-menu { max-height: 75vh; overflow-y: auto; > li > a { - // &.focus { - // &:extend(.dropdown-menu>li>a:focus); - // } &:focus { outline: none; } diff --git a/public/scss/admin/overrides.scss b/public/scss/admin/overrides.scss new file mode 100644 index 0000000000..58922bd310 --- /dev/null +++ b/public/scss/admin/overrides.scss @@ -0,0 +1,62 @@ +$white: #fff !default; +$gray-100: #f8f9fa !default; +$gray-200: #e9ecef !default; +$gray-300: #dee2e6 !default; +$gray-400: #ced4da !default; +$gray-500: #adb5bd !default; +$gray-600: #6c757d !default; +$gray-700: #495057 !default; +$gray-800: #343a40 !default; +$gray-900: #212529 !default; +$black: #000 !default; + +$blue: #0d6efd !default; +$red: #dc3545 !default; +$yellow: #ffc107 !default; +$green: #198754 !default; +$cyan: #0dcaf0 !default; + +$body-color: $gray-800; +$text-muted: $gray-600 !default; + +// Custom fonts +$font-family-sans-serif: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + +$font-family-secondary: "Poppins", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default; +$font-weight-semibold: 500 !default; +$font-size-base: 1rem !default; + +$link-decoration: none; +$link-hover-decoration: underline; + +// options +$enable-gradients: false; + +// no caret on dropdown-toggle +$enable-caret: false; + +// disable smooth scroll, this makes window.scrollTo(0,0) in ajaxify.js take x milliseconds +$enable-smooth-scroll: false; + +$enable-shadows: true; + + +// Buttons +$input-btn-padding-y: .4rem !default; +$input-btn-padding-x: 1rem !default; + +// Forms + +$input-padding-y: 0.5rem !default; +$input-padding-x: 0.75rem !default; +// $input-padding-y-sm: 0 !default; +// $input-padding-x-sm: 0 !default; +// $input-padding-y-lg: ($font-size-base * 1.25) !default; +// $input-padding-x-lg: 0 !default; + +$input-btn-focus-color: #c29ffa80 !default; +$input-border-radius: 0.25rem !default; +$input-focus-border-color: #c29ffa80 !default; +// change inset shadow to outset +$box-shadow-inset: 0 1px 2px rgba($black, .075) !default; + diff --git a/public/scss/admin/settings.scss b/public/scss/admin/settings.scss index e760d71787..e69de29bb2 100644 --- a/public/scss/admin/settings.scss +++ b/public/scss/admin/settings.scss @@ -1,20 +0,0 @@ -.settings { - .section-content { - border-left: 3px solid $primary; - - ul { - list-style-type: none; - font-size: 16px; - padding-left: 20px; - li:not(:last-child) { - margin-bottom: 5px; - } - a { - text-decoration: none; - &:hover{ - text-decoration: underline; - } - } - } - } -} \ No newline at end of file diff --git a/public/scss/admin/sidebar.scss b/public/scss/admin/sidebar.scss new file mode 100644 index 0000000000..0a354fdd6c --- /dev/null +++ b/public/scss/admin/sidebar.scss @@ -0,0 +1,13 @@ +#sidebar-left, #offcanvas { + .btn-ghost, .btn-ghost-sm { + i { + color: $gray-500; + } + } + + .accordion-body { + .btn-ghost-sm { + padding-left: 38px!important; + } + } +} \ No newline at end of file diff --git a/public/scss/admin/vars.scss b/public/scss/admin/vars.scss deleted file mode 100644 index e5330b8924..0000000000 --- a/public/scss/admin/vars.scss +++ /dev/null @@ -1,45 +0,0 @@ -// system font family -// based on those in [bootstrap@5.0.0-alpha1](https://github.com/twbs/bootstrap/blob/b531bda07cbea2e124194aefe3b8597b3ac2578e/scss/_variables.scss#L386) -// and [wordpress admin](https://core.trac.wordpress.org/browser/trunk/src/wp-admin/css/common.css?rev=47835#L220) -// system-ui : supported by the latest browsers for this very purpose -// apple-system, BlinkMacSystemFont : iOS and MacOS -// "Segoe UI" : Windows Vista, 7, 8, 10 -// Roboto : Android 4.0+ -// Oxygen-Sans : KDE -// Ubuntu : Ubuntu -// Cantarell : GNOME -// "Helvetica Neue" : Mac OS X -// Helvetica : backup, better looking than Arial -// Arial : backup -// "Noto Sans" : broader language support on Android -// sans-serif : whatever the browser can give us -// "Apple Color Emoji" : Emoji on iOS and MacOS -// "Segoe UI Emoji", "Segoe UI Symbol" : Emoji on Windows -// "Noto Color Emoji" : Emoji on Android -$font-family-system: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -$font-family-sans-serif: $font-family-system; -$font-size-base: 0.875rem!default; - -$body-color: #666!default; -$light: #f5f5f5; - -// options -$enable-gradients: false; -// no caret on dropdown-toggle -$enable-caret: false; - -$font-size-base: 1rem !default; -$font-weight-base: 400 !default; - -// Buttons -$input-btn-padding-y: .4rem !default; -$input-btn-padding-x: 1rem !default; - -// Forms - -$input-padding-y: 0.4rem !default; -$input-padding-x: 0 !default; -$input-padding-y-sm: 0 !default; -$input-padding-x-sm: 0 !default; -$input-padding-y-lg: ($font-size-base * 1.25) !default; -$input-padding-x-lg: 0 !default; \ No newline at end of file diff --git a/public/src/admin/admin.js b/public/src/admin/admin.js index d65e291ea7..d39046d543 100644 --- a/public/src/admin/admin.js +++ b/public/src/admin/admin.js @@ -42,9 +42,17 @@ app.onDomReady(); } require(['hooks'], (hooks) => { - hooks.on('action:ajaxify.end', () => { + hooks.on('action:ajaxify.end', (data) => { + updatePageTitle(data.url); + setupRestartLinks(); showCorrectNavTab(); startLogoutTimer(); + + $('[data-bs-toggle="tooltip"]').tooltip({ + animation: false, + container: '#content', + }); + if ($('.settings').length) { require(['admin/settings'], function (Settings) { Settings.prepare(); @@ -52,21 +60,35 @@ app.onDomReady(); }); } }); + hooks.on('action:ajaxify.start', function () { + require(['bootstrap'], function (boostrap) { + const offcanvas = boostrap.Offcanvas.getInstance('#offcanvas'); + if (offcanvas) { + offcanvas.hide(); + } + }); + }); }); function showCorrectNavTab() { - // show correct tab if url has # - if (window.location.hash) { - $('.nav-pills a[href="' + window.location.hash + '"]').tab('show'); + const accordionEl = $('[component="acp/accordion"]'); + let pathname = window.location.pathname; + if (pathname === '/admin') { + pathname = '/admin/dashboard'; + } + const selectedButton = accordionEl.find(`a[href="${pathname}"]`); + if (selectedButton.length) { + accordionEl.find('a').removeClass('active'); + accordionEl.find('.accordion-collapse').removeClass('show'); + selectedButton.addClass('active'); + selectedButton.parents('.accordion-collapse').addClass('show'); } } $(document).ready(function () { - if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { - require(['admin/modules/search'], function (search) { - search.init(); - }); - } + require(['admin/modules/search'], function (search) { + search.init(); + }); $('[component="logout"]').on('click', function () { require(['logout'], function (logout) { @@ -75,16 +97,25 @@ app.onDomReady(); return false; }); - configureSlidemenu(); setupNProgress(); + fixAccordionIds(); }); - $(window).on('action:ajaxify.contentLoaded', function (ev, data) { - selectMenuItem(data.url); - setupRestartLinks(); - require('material-design-lite'); - componentHandler.upgradeDom(); - }); + function fixAccordionIds() { + // fix mobile accordion, so it doesn't have same ids as desktop + // the same accordion partial is used in both places + const offcanvasAccordion = $('#offcanvas #accordionACP'); + offcanvasAccordion.attr('id', 'accordionACP-offcanvas'); + offcanvasAccordion.find('[data-bs-target]').each((i, el) => { + $(el).attr('data-bs-target', $(el).attr('data-bs-target') + '-offcanvas'); + }); + offcanvasAccordion.find('[data-bs-parent]').each((i, el) => { + $(el).attr('data-bs-parent', '#accordionACP-offcanvas'); + }); + offcanvasAccordion.find('.accordion-collapse').each((i, el) => { + $(el).attr('id', $(el).attr('id') + '-offcanvas'); + }); + } function setupNProgress() { require(['nprogress', 'hooks'], function (NProgress, hooks) { @@ -98,7 +129,7 @@ app.onDomReady(); }); } - function selectMenuItem(url) { + function updatePageTitle(url) { require(['translator'], function (translator) { url = url .replace(/\/\d+$/, '') @@ -113,17 +144,8 @@ app.onDomReady(); url = [config.relative_path, url].join('/'); let fallback; - $('#main-menu li').removeClass('active'); - $('#main-menu a').removeClass('active').filter('[href="' + url + '"]').each(function () { - const menu = $(this); - if (menu.parent().attr('data-link')) { - return; - } - - menu - .parent().addClass('active') - .parents('.menu-item').addClass('active'); - fallback = menu.text(); + $(`[component="acp/accordion"] a[href="${url}"]`).each(function () { + fallback = $(this).text(); }); let mainTitle; @@ -152,9 +174,6 @@ app.onDomReady(); translator.translate(pageTitle, function (title) { document.title = title.replace(/>/g, '>'); }); - translator.translate(mainTitle, function (text) { - $('#main-page-title').text(text); - }); }); } @@ -164,7 +183,7 @@ app.onDomReady(); // otherwise it can be unloaded when rebuild & restart is run // the client can't fetch the template file, resulting in an error benchpress.render('partials/toast', {}).then(function () { - $('.rebuild-and-restart').off('click').on('click', function () { + $('[component="rebuild-and-restart"]').off('click').on('click', function () { bootbox.confirm('[[admin/admin:alert.confirm-rebuild-and-restart]]', function (confirm) { if (confirm) { instance.rebuildAndRestart(); @@ -172,7 +191,7 @@ app.onDomReady(); }); }); - $('.restart').off('click').on('click', function () { + $('[component="restart"]').off('click').on('click', function () { bootbox.confirm('[[admin/admin:alert.confirm-restart]]', function (confirm) { if (confirm) { instance.restart(); @@ -182,62 +201,4 @@ app.onDomReady(); }); }); } - - function configureSlidemenu() { - require(['slideout'], function (Slideout) { - let env = utils.findBootstrapEnvironment(); - - const slideout = new Slideout({ - panel: document.getElementById('panel'), - menu: document.getElementById('menu'), - padding: 256, - tolerance: 70, - }); - - if (env === 'md' || env === 'lg') { - slideout.disableTouch(); - } - - $('#mobile-menu').on('click', function () { - slideout.toggle(); - }); - - $('#menu a').on('click', function () { - slideout.close(); - }); - - $(window).on('resize', function () { - slideout.close(); - - env = utils.findBootstrapEnvironment(); - if (['xxl', 'xl', 'lg'].includes(env)) { - slideout.disableTouch(); - $('#header').css({ - position: 'relative', - }); - } else { - slideout.enableTouch(); - $('#header').css({ - position: 'fixed', - }); - } - }); - - function onOpeningMenu() { - $('#header').css({ - top: ($('#panel').position().top * -1) + 'px', - position: 'absolute', - }); - } - - slideout.on('open', onOpeningMenu); - - slideout.on('close', function () { - $('#header').css({ - top: '0px', - position: 'fixed', - }); - }); - }); - } }()); diff --git a/public/src/admin/dashboard.js b/public/src/admin/dashboard.js index d8bdd5386d..5cab8c9c35 100644 --- a/public/src/admin/dashboard.js +++ b/public/src/admin/dashboard.js @@ -43,10 +43,6 @@ define('admin/dashboard', [ isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); - $('[data-bs-toggle="tooltip"]').tooltip({ - animation: false, - }); - setupRealtimeButton(); setupGraphs(function () { socket.emit('admin.rooms.getAll', Admin.updateRoomUsage); @@ -68,19 +64,19 @@ define('admin/dashboard', [ const html = '
' + '' + data.onlineRegisteredCount + '' + - '
[[admin/dashboard:active-users.users]]
' + + '
[[admin/dashboard:active-users.users]]
' + '
' + '
' + '' + data.onlineGuestCount + '' + - '
[[admin/dashboard:active-users.guests]]
' + + '
[[admin/dashboard:active-users.guests]]
' + '
' + '
' + '' + (data.onlineRegisteredCount + data.onlineGuestCount) + '' + - '
[[admin/dashboard:active-users.total]]
' + + '
[[admin/dashboard:active-users.total]]
' + '
' + '
' + '' + data.socketCount + '' + - '
[[admin/dashboard:active-users.connections]]
' + + '
[[admin/dashboard:active-users.connections]]
' + '
'; updateRegisteredGraph(data.onlineRegisteredCount, data.onlineGuestCount); diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index 6cd34a26d3..4d3d009d63 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -31,7 +31,8 @@ define('admin/extend/plugins', [ pluginsList.on('click', 'button[data-action="toggleActive"]', function () { const pluginEl = $(this).parents('li'); pluginID = pluginEl.attr('data-plugin-id'); - const btn = $('[id="' + pluginID + '"] [data-action="toggleActive"]'); + // const btn = $('[id="' + pluginID + '"] [data-action="toggleActive"]'); + const btn = $(this); const pluginData = ajaxify.data.installed[pluginEl.attr('data-plugin-index')]; @@ -40,30 +41,28 @@ define('admin/extend/plugins', [ if (err) { return alerts.error(err); } - translator.translate(' [[admin/extend/plugins:plugin-item.' + (status.active ? 'deactivate' : 'activate') + ']]', function (buttonText) { - btn.html(buttonText); - btn.toggleClass('btn-warning', status.active).toggleClass('btn-success', !status.active); + btn.siblings('[data-action="toggleActive"]').removeClass('hidden'); + btn.addClass('hidden'); - // clone it to active plugins tab - if (status.active && !$('#active [id="' + pluginID + '"]').length) { - $('#active ul').prepend(pluginEl.clone(true)); - } + // clone it to active plugins tab + if (status.active && !$('#active [id="' + pluginID + '"]').length) { + $('#active ul').prepend(pluginEl.clone(true)); + } - // Toggle active state in template data - pluginData.active = !pluginData.active; - - alerts.alert({ - alert_id: 'plugin_toggled', - title: '[[admin/extend/plugins:alert.' + (status.active ? 'enabled' : 'disabled') + ']]', - message: '[[admin/extend/plugins:alert.' + (status.active ? 'activate-success' : 'deactivate-success') + ']]', - type: status.active ? 'warning' : 'success', - timeout: 5000, - clickfn: function () { - require(['admin/modules/instance'], function (instance) { - instance.rebuildAndRestart(); - }); - }, - }); + // Toggle active state in template data + pluginData.active = !pluginData.active; + + alerts.alert({ + alert_id: 'plugin_toggled', + title: '[[admin/extend/plugins:alert.' + (status.active ? 'enabled' : 'disabled') + ']]', + message: '[[admin/extend/plugins:alert.' + (status.active ? 'activate-success' : 'deactivate-success') + ']]', + type: status.active ? 'warning' : 'success', + timeout: 5000, + clickfn: function () { + require(['admin/modules/instance'], function (instance) { + instance.rebuildAndRestart(); + }); + }, }); }); } diff --git a/public/src/admin/extend/rewards.js b/public/src/admin/extend/rewards.js index 900ff3549a..f8f2deb478 100644 --- a/public/src/admin/extend/rewards.js +++ b/public/src/admin/extend/rewards.js @@ -95,16 +95,16 @@ define('admin/extend/rewards', ['alerts'], function (alerts) { html += '
'; + html += ''; }); div.html(html); diff --git a/public/src/admin/manage/categories.js b/public/src/admin/manage/categories.js index 25479a0879..a2ec91f1fd 100644 --- a/public/src/admin/manage/categories.js +++ b/public/src/admin/manage/categories.js @@ -15,12 +15,14 @@ define('admin/manage/categories', [ let sortables; Categories.init = function () { - categorySelector.init($('.category [component="category-selector"]'), { + categorySelector.init($('[component="category-selector"]'), { parentCid: ajaxify.data.selectedCategory ? ajaxify.data.selectedCategory.cid : 0, onSelect: function (selectedCategory) { ajaxify.go('/admin/manage/categories' + (selectedCategory.cid ? '?cid=' + selectedCategory.cid : '')); }, + cacheList: false, localCategories: [], + template: 'admin/partials/category/selector-dropdown-right', }); Categories.render(ajaxify.data.categoriesTree); @@ -74,12 +76,14 @@ define('admin/manage/categories', [ }); }); - $('#collapse-all').on('click', function () { - toggleAll(false); - }); - - $('#expand-all').on('click', function () { - toggleAll(true); + $('#toggle-collapse-all').on('click', function () { + const $this = $(this); + const isCollapsed = parseInt($this.attr('data-collapsed'), 10) === 1; + toggleAll(isCollapsed); + $this.attr('data-collapsed', isCollapsed ? 0 : 1) + .translateText(isCollapsed ? + '[[admin/manage/categories:collapse-all]]' : + '[[admin/manage/categories:expand-all]]'); }); function toggleAll(expand) { @@ -96,7 +100,7 @@ define('admin/manage/categories', [ message: html, buttons: { save: { - label: '[[global:save]]', + label: '[[global:create]]', className: 'btn-primary', callback: submit, }, @@ -110,6 +114,7 @@ define('admin/manage/categories', [ icon: 'fa-none', }, ], + template: 'admin/partials/category/selector-dropdown-left', }; const parentSelector = categorySelector.init(modal.find('#parentCidGroup [component="category-selector"]'), options); const cloneFromSelector = categorySelector.init(modal.find('#cloneFromCidGroup [component="category-selector"]'), options); @@ -177,7 +182,7 @@ define('admin/manage/categories', [ }; Categories.toggle = function (cids, disabled) { - const listEl = document.querySelector('.categories ul'); + const listEl = document.querySelector('.categories [data-cid="0"]'); Promise.all(cids.map(cid => api.put('/categories/' + cid, { disabled: disabled ? 1 : 0, }).then(() => { @@ -214,14 +219,14 @@ define('admin/manage/categories', [ if (oldParentCid !== newParentCid) { const toggle = document.querySelector(`.categories li[data-cid="${newParentCid}"] .toggle`); if (toggle) { - toggle.classList.toggle('hide', false); + toggle.classList.toggle('invisible', false); } const children = document.querySelectorAll(`.categories li[data-cid="${oldParentCid}"] ul[data-cid] li[data-cid]`); if (!children.length) { const toggle = document.querySelector(`.categories li[data-cid="${oldParentCid}"] .toggle`); if (toggle) { - toggle.classList.toggle('hide', true); + toggle.classList.toggle('invisible', true); } } @@ -278,7 +283,7 @@ define('admin/manage/categories', [ // Disable expand toggle if (!categories.length) { const toggleEl = container.get(0).querySelector('.toggle'); - toggleEl.classList.toggle('hide', true); + toggleEl.classList.toggle('invisible', true); } // Handle and children categories in this level have diff --git a/public/src/admin/manage/category.js b/public/src/admin/manage/category.js index 065c1dcaeb..9703c6c888 100644 --- a/public/src/admin/manage/category.js +++ b/public/src/admin/manage/category.js @@ -14,23 +14,51 @@ define('admin/manage/category', [ let updateHash = {}; Category.init = function () { - $('#category-settings select').each(function () { + const categorySettings = $('#category-settings'); + const previewEl = $('[component="category/preview"]'); + categorySettings.find('select').each(function () { const $this = $(this); $this.val($this.attr('data-value')); }); - categorySelector.init($('[component="category-selector"]'), { + // category switcher + categorySelector.init($('[component="settings/main/header"] [component="category-selector"]'), { onSelect: function (selectedCategory) { ajaxify.go('admin/manage/categories/' + selectedCategory.cid); }, + cacheList: false, showLinks: true, + template: 'admin/partials/category/selector-dropdown-right', + }); + + // parent selector + categorySelector.init($('#parent-category-selector [component="category-selector"]'), { + onSelect: function (selectedCategory) { + const parentCidInput = $('#parent-cid'); + parentCidInput.val(selectedCategory.cid); + modified(parentCidInput[0]); + }, + selectedCategory: ajaxify.data.category.parent, // switch selection to parent + localCategories: [ + { + cid: 0, + name: '[[admin/manage/categories:parent-category-none]]', + icon: 'fa-list', + }, + ], + cacheList: false, + showLinks: true, + template: 'admin/partials/category/selector-dropdown-right', }); handleTags(); - $('#category-settings input, #category-settings select, #category-settings textarea').on('change', function (ev) { + categorySettings.find('input, select, textarea').on('change', function (ev) { modified(ev.target); }); + $('[type="checkbox"]').on('change', function () { + modified($(this)); + }); $('[data-name="imageClass"]').on('change', function () { $('.category-preview').css('background-size', $(this).val()); @@ -38,7 +66,6 @@ define('admin/manage/category', [ $('[data-name="bgColor"], [data-name="color"]').on('input', function () { const $inputEl = $(this); - const previewEl = $inputEl.parents('[data-cid]').find('.category-preview'); if ($inputEl.attr('data-name') === 'bgColor') { previewEl.css('background-color', $inputEl.val()); } else if ($inputEl.attr('data-name') === 'color') { @@ -171,56 +198,41 @@ define('admin/manage/category', [ params: { cid: cid }, }, function (imageUrlOnServer) { $('#category-image').val(imageUrlOnServer); - const previewBox = inputEl.parent().parent().siblings('.category-preview'); - previewBox.css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')'); + previewEl.css('background-image', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')'); modified($('#category-image')); }); }); $('#category-image').on('change', function () { - $('.category-preview').css('background-image', $(this).val() ? ('url("' + $(this).val() + '")') : ''); + previewEl.css('background-image', $(this).val() ? ('url("' + $(this).val() + '")') : ''); modified($('#category-image')); }); $('.delete-image').on('click', function (e) { e.preventDefault(); - const inputEl = $('#category-image'); - const previewBox = $('.category-preview'); - inputEl.val(''); - previewBox.css('background-image', ''); + previewEl.css('background-image', ''); modified(inputEl[0]); - $(this).parent().addClass('hide').hide(); }); - $('.category-preview').on('click', function () { + previewEl.on('click', function () { iconSelect.init($(this).find('i'), modified); }); - $('[type="checkbox"]').on('change', function () { - modified($(this)); - }); - - $('button[data-action="setParent"], button[data-action="changeParent"]').on('click', Category.launchParentSelector); - $('button[data-action="removeParent"]').on('click', function () { - api.put('/categories/' + ajaxify.data.category.cid, { - parentCid: 0, - }).then(() => { - $('button[data-action="removeParent"]').parent().addClass('hide'); - $('button[data-action="changeParent"]').parent().addClass('hide'); - $('button[data-action="setParent"]').removeClass('hide'); - }).catch(alerts.error); - }); $('button[data-action="toggle"]').on('click', function () { const $this = $(this); const disabled = $this.attr('data-disabled') === '1'; api.put('/categories/' + ajaxify.data.category.cid, { disabled: disabled ? 0 : 1, }).then(() => { - $this.translateText(!disabled ? '[[admin/manage/categories:enable]]' : '[[admin/manage/categories:disable]]'); - $this.toggleClass('btn-primary', !disabled).toggleClass('btn-danger', disabled); + $this.find('.label').translateText( + !disabled ? '[[admin/manage/categories:enable]]' : '[[admin/manage/categories:disable]]' + ); + $this.find('i') + .toggleClass(['fa-check', 'text-success'], !disabled) + .toggleClass(['fa-ban', 'text-danger'], disabled); $this.attr('data-disabled', disabled ? 0 : 1); }).catch(alerts.error); }); @@ -276,30 +288,5 @@ define('admin/manage/category', [ }); } - Category.launchParentSelector = function () { - categorySelector.modal({ - onSubmit: function (selectedCategory) { - const parentCid = selectedCategory.cid; - if (!parentCid) { - return; - } - api.put('/categories/' + ajaxify.data.category.cid, { - parentCid: parentCid, - }).then(() => { - api.get(`/categories/${parentCid}`, {}).then(function (parent) { - if (parent && parent.icon && parent.name) { - const buttonHtml = ' ' + parent.name; - $('button[data-action="changeParent"]').html(buttonHtml).parent().removeClass('hide'); - } - }); - - $('button[data-action="removeParent"]').parent().removeClass('hide'); - $('button[data-action="setParent"]').addClass('hide'); - }).catch(alerts.error); - }, - showLinks: true, - }); - }; - return Category; }); diff --git a/public/src/admin/manage/group.js b/public/src/admin/manage/group.js index 4c73451a37..4a95bd7ea8 100644 --- a/public/src/admin/manage/group.js +++ b/public/src/admin/manage/group.js @@ -47,7 +47,7 @@ define('admin/manage/group', [ setupGroupMembersMenu(); - $('#group-icon, #group-icon-label').on('click', function () { + $('#group-icon-container').on('click', function () { const currentIcon = groupIcon.attr('value'); iconSelect.init(groupIcon, function () { let newIcon = groupIcon.attr('value'); @@ -77,6 +77,7 @@ define('admin/manage/group', [ cids = cids.filter((cid, index, array) => array.indexOf(cid) === index); $('#memberPostCids').val(cids.join(',')); cidSelector.selectCategory(0); + return false; }, }); @@ -87,6 +88,16 @@ define('admin/manage/group', [ app.flags._unsaved = true; }); + $('[data-action="delete"]').on('click', function () { + bootbox.confirm('[[admin/manage/groups:alerts.confirm-delete]]', function (confirm) { + if (confirm) { + api.del(`/groups/${slugify(ajaxify.data.group.name)}`, {}).then(() => { + ajaxify.go('/admin/managegroups'); + }).catch(alerts.error); + } + }); + }); + $('#save').on('click', function () { api.put(`/groups/${slugify(groupName)}`, { name: $('#change-group-name').val(), diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index 11b079ac3b..682dcadf21 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -30,22 +30,8 @@ define('admin/manage/groups', [ break; } }); - - enableCategorySelectors(); }; - function enableCategorySelectors() { - $('.groups-list [component="category-selector"]').each(function () { - const nameEncoded = $(this).parents('[data-name-encoded]').attr('data-name-encoded'); - categorySelector.init($(this), { - onSelect: function (selectedCategory) { - ajaxify.go('admin/manage/privileges/' + selectedCategory.cid + '?group=' + nameEncoded); - }, - showLinks: true, - }); - }); - } - function handleCreate() { $('#create').on('click', function () { app.parseAndTranslate('admin/partials/create_group_modal', {}).then((html) => { @@ -119,7 +105,6 @@ define('admin/manage/groups', [ }, function (html) { groupsEl.find('[data-groupname]').remove(); groupsEl.find('tbody').append(html); - enableCategorySelectors(); }); }); } diff --git a/public/src/admin/modules/search.js b/public/src/admin/modules/search.js index 16fe21a26d..44394afbdf 100644 --- a/public/src/admin/modules/search.js +++ b/public/src/admin/modules/search.js @@ -21,7 +21,7 @@ define('admin/modules/search', ['mousetrap', 'alerts'], function (mousetrap, ale // and wrap the match in a `.search-match` element .replace( new RegExp('^[\\s\\S]*?(.{0,25})(' + escaped + ')(.{0,25})[\\s\\S]*?$', 'gmi'), - '...$1$2$3...
' + '...$1$2$3...
' ) // collapse whitespace .replace(/(?:\n ?)+/g, '\n') @@ -33,7 +33,7 @@ define('admin/modules/search', ['mousetrap', 'alerts'], function (mousetrap, ale ); return '
  • ' + header + '
  • '); + new bootstrap.ScrollSpy($('#spy-container')[0], { + target: '#settings-navbar', + rootMargin: '-10% 0px -70%', + smoothScroll: true, }); - const scrollTo = $('a[name="' + window.location.hash.replace('#', '') + '"]'); + const scrollTo = $(`${window.location.hash}`); if (scrollTo.length) { $('html, body').animate({ - scrollTop: (scrollTo.offset().top) + 'px', + scrollTop: (scrollTo.offset().top - offset) + 'px', }, 400); } - } else { - $('.content-header').parents('.row').remove(); + tocEl.removeClass('hidden'); } }; @@ -117,7 +143,7 @@ define('admin/settings', ['uploader', 'mousetrap', 'hooks', 'alerts', 'settings' saveBtnEl.classList.toggle('saved', true); setTimeout(() => { saveBtnEl.classList.toggle('saved', false); - }, 5000); + }, 2500); } }; diff --git a/public/src/admin/settings/general.js b/public/src/admin/settings/general.js index 805f9f1c8f..65f1f0bce4 100644 --- a/public/src/admin/settings/general.js +++ b/public/src/admin/settings/general.js @@ -20,7 +20,19 @@ define('admin/settings/general', ['admin/settings'], function () { $('button[data-action="removeOgImage"]').on('click', function () { $('input[data-field="removeOgImage"]').val(''); }); + + $('[data-field="homePageRoute"]').on('change', toggleCustomRoute); + + toggleCustomRoute(); }; + function toggleCustomRoute() { + if ($('[data-field="homePageRoute"]').val() === 'custom') { + $('#homePageCustom').show(); + } else { + $('#homePageCustom').hide(); + } + } + return Module; }); diff --git a/public/src/admin/settings/homepage.js b/public/src/admin/settings/homepage.js deleted file mode 100644 index 96447d9bbb..0000000000 --- a/public/src/admin/settings/homepage.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - - -define('admin/settings/homepage', ['admin/settings'], function () { - function toggleCustomRoute() { - if ($('[data-field="homePageRoute"]').val() === 'custom') { - $('#homePageCustom').show(); - } else { - $('#homePageCustom').hide(); - } - } - - const Homepage = {}; - - Homepage.init = function () { - $('[data-field="homePageRoute"]').on('change', toggleCustomRoute); - - toggleCustomRoute(); - }; - - return Homepage; -}); diff --git a/public/src/admin/settings/navigation.js b/public/src/admin/settings/navigation.js index 6ae9fa1918..c858f8ccc4 100644 --- a/public/src/admin/settings/navigation.js +++ b/public/src/admin/settings/navigation.js @@ -163,13 +163,12 @@ define('admin/settings/navigation', [ function toggle() { const btn = $(this); - const disabled = btn.hasClass('btn-success'); + const disabled = btn.hasClass('enable'); const index = btn.parents('[data-index]').attr('data-index'); - translator.translate(disabled ? '[[admin/settings/navigation:btn.disable]]' : '[[admin/settings/navigation:btn.enable]]', function (html) { - btn.toggleClass('btn-warning').toggleClass('btn-success').html(html); - btn.parents('li').find('[name="enabled"]').val(disabled ? 'on' : ''); - $('#active-navigation [data-index="' + index + '"] a').toggleClass('text-muted', !disabled); - }); + btn.siblings('.toggle').removeClass('hidden'); + btn.addClass('hidden'); + btn.parents('li').find('[name="enabled"]').val(disabled ? 'on' : ''); + $('#active-navigation [data-index="' + index + '"] a').toggleClass('text-muted', !disabled); return false; } diff --git a/public/src/admin/settings/social.js b/public/src/admin/settings/social.js deleted file mode 100644 index 20e6f87a9f..0000000000 --- a/public/src/admin/settings/social.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - - -define('admin/settings/social', ['alerts'], function (alerts) { - const social = {}; - - social.init = function () { - $('#save').on('click', function () { - const networks = []; - $('#postSharingNetworks input[type="checkbox"]').each(function () { - if ($(this).prop('checked')) { - networks.push($(this).attr('id')); - } - }); - - socket.emit('admin.social.savePostSharingNetworks', networks, function (err) { - if (err) { - return alerts.error(err); - } - - alerts.success('[[admin/settings/social:save-success]]'); - }); - }); - }; - - return social; -}); diff --git a/public/src/client/groups/memberlist.js b/public/src/client/groups/memberlist.js index 7a3376eb51..2b8bce8107 100644 --- a/public/src/client/groups/memberlist.js +++ b/public/src/client/groups/memberlist.js @@ -22,7 +22,8 @@ define('forum/groups/memberlist', ['api', 'bootbox', 'alerts'], function (api, b title: '[[groups:details.add-member]]', message: html, buttons: { - ok: { + OK: { + label: '[[groups:details.add-member]]', callback: function () { const users = []; modal.find('[data-uid][data-selected]').each(function (index, el) { diff --git a/public/src/modules/categoryFilter.js b/public/src/modules/categoryFilter.js index 271bf83448..169242349b 100644 --- a/public/src/modules/categoryFilter.js +++ b/public/src/modules/categoryFilter.js @@ -9,7 +9,7 @@ define('categoryFilter', ['categorySearch', 'api', 'hooks'], function (categoryS } options = options || {}; options.states = options.states || ['watching', 'notwatching', 'ignoring']; - options.template = 'partials/category/filter-dropdown-left'; + options.template = options.template || 'partials/category/filter-dropdown-left'; hooks.fire('action:category.filter.options', { el: el, options: options }); diff --git a/public/src/modules/categorySearch.js b/public/src/modules/categorySearch.js index 608ed21275..5dccf8308a 100644 --- a/public/src/modules/categorySearch.js +++ b/public/src/modules/categorySearch.js @@ -8,6 +8,7 @@ define('categorySearch', ['alerts', 'bootstrap'], function (alerts, bootstrap) { options = options || {}; options.privilege = options.privilege || 'topics:read'; options.states = options.states || ['watching', 'notwatching', 'ignoring']; + options.cacheList = options.hasOwnProperty('cacheList') ? options.cacheList : true; let localCategories = []; if (Array.isArray(options.localCategories)) { @@ -36,7 +37,7 @@ define('categorySearch', ['alerts', 'bootstrap'], function (alerts, bootstrap) { const val = searchEl.find('input').val(); if (val.length > 1 || (!val && !categoriesList)) { loadList(val, function (categories) { - categoriesList = categoriesList || categories; + categoriesList = options.cacheList && (categoriesList || categories); renderList(categories); }); } else if (!val && categoriesList) { diff --git a/public/src/modules/categorySelector.js b/public/src/modules/categorySelector.js index 1713c28747..3e2bfa5982 100644 --- a/public/src/modules/categorySelector.js +++ b/public/src/modules/categorySelector.js @@ -1,8 +1,8 @@ 'use strict'; define('categorySelector', [ - 'categorySearch', 'bootbox', 'hooks', -], function (categorySearch, bootbox, hooks) { + 'categorySearch', 'bootbox', 'hooks', 'translator', +], function (categorySearch, bootbox, hooks, translator) { const categorySelector = {}; categorySelector.init = function (el, options) { @@ -13,7 +13,7 @@ define('categorySelector', [ const onSelect = options.onSelect || function () {}; options.states = options.states || ['watching', 'notwatching', 'ignoring']; - options.template = 'partials/category/selector-dropdown-left'; + options.template = options.template || 'partials/category/selector-dropdown-left'; hooks.fire('action:category.selector.options', { el: el, options: options }); categorySearch.init(el, options); @@ -22,15 +22,21 @@ define('categorySelector', [ el: el, selectedCategory: null, }; + el.on('click', '[data-cid]', function () { const categoryEl = $(this); if (categoryEl.hasClass('disabled')) { return false; } selector.selectCategory(categoryEl.attr('data-cid')); - onSelect(selector.selectedCategory); + return onSelect(selector.selectedCategory); + }); + + let defaultSelectHtml = selector.el.find('[component="category-selector-selected"]').html(); + + translator.translate(defaultSelectHtml, (translated) => { + defaultSelectHtml = translated; }); - const defaultSelectHtml = selector.el.find('[component="category-selector-selected"]').html(); selector.selectCategory = function (cid) { const categoryEl = selector.el.find('[data-cid="' + cid + '"]'); selector.selectedCategory = { @@ -54,6 +60,14 @@ define('categorySelector', [ selector.getSelectedCid = function () { return selector.selectedCategory ? selector.selectedCategory.cid : 0; }; + + if (options.hasOwnProperty('selectedCategory')) { + app.parseAndTranslate(options.template, { selectedCategory: options.selectedCategory }, function (html) { + selector.el.find('[component="category-selector-selected"]').html( + html.find('[component="category-selector-selected"]').html() + ); + }); + } return selector; }; diff --git a/public/src/modules/iconSelect.js b/public/src/modules/iconSelect.js index b85fd5af1c..3795c00e23 100644 --- a/public/src/modules/iconSelect.js +++ b/public/src/modules/iconSelect.js @@ -323,7 +323,7 @@ define('iconSelect', ['benchpress', 'bootbox'], function (Benchpress, bootbox) { } icons.remove(); iconData.forEach((iconData) => { - iconContainer.append($(``)); + iconContainer.append($(``)); }); icons = modalEl.find('.nbb-fa-icons i'); changeSelection(); diff --git a/src/categories/index.js b/src/categories/index.js index 9fc7479a69..3fe5d9d457 100644 --- a/src/categories/index.js +++ b/src/categories/index.js @@ -364,6 +364,13 @@ Categories.buildForSelectCategories = function (categories, fields, parentCid) { const rootCategories = categories.filter(category => category && category.parentCid === parentCid); + rootCategories.sort((a, b) => { + if (a.order !== b.order) { + return a.order - b.order; + } + return a.cid - b.cid; + }); + rootCategories.forEach(category => recursive(category, categoriesData, '', 0)); const pickFields = [ diff --git a/src/controllers/admin/categories.js b/src/controllers/admin/categories.js index d901e65f0f..597f8ff55e 100644 --- a/src/controllers/admin/categories.js +++ b/src/controllers/admin/categories.js @@ -60,8 +60,9 @@ categoriesController.getAll = async function (req, res) { } const fields = [ - 'cid', 'name', 'icon', 'parentCid', 'disabled', 'link', 'order', - 'color', 'bgColor', 'backgroundImage', 'imageClass', 'subCategoriesPerPage', + 'cid', 'name', 'icon', 'parentCid', 'disabled', 'link', + 'order', 'color', 'bgColor', 'backgroundImage', 'imageClass', + 'subCategoriesPerPage', 'description', ]; const categoriesData = await categories.getCategoriesFields(cids, fields); const result = await plugins.hooks.fire('filter:admin.categories.get', { categories: categoriesData, fields: fields }); @@ -101,6 +102,7 @@ categoriesController.getAll = async function (req, res) { breadcrumbs: crumbs, pagination: pagination.create(page, pageCount, req.query), categoriesPerPage: meta.config.categoriesPerPage, + selectCategoryLabel: '[[admin/manage/categories:jump-to]]', }); }; diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index e13511816c..210c6ce614 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -18,13 +18,27 @@ const settingsController = module.exports; settingsController.get = async function (req, res) { const term = req.params.term || 'general'; - res.render(`admin/settings/${term}`); + const payload = { + title: `[[admin/menu:settings/${term}]]`, + }; + if (term === 'general') { + payload.routes = await helpers.getHomePageRoutes(req.uid); + payload.postSharing = await social.getPostSharing(); + const languageData = await languages.list(); + languageData.forEach((language) => { + language.selected = language.code === meta.config.defaultLang; + }); + payload.languages = languageData; + payload.autoDetectLang = meta.config.autoDetectLang; + } + res.render(`admin/settings/${term}`, payload); }; settingsController.email = async (req, res) => { const emails = await emailer.getTemplates(meta.config); res.render('admin/settings/email', { + title: '[[admin/menu:settings/email]]', emails: emails, sendable: emails.filter(e => !e.path.includes('_plaintext') && !e.path.includes('partials')).map(tpl => tpl.path), services: emailer.listServices(), @@ -38,6 +52,7 @@ settingsController.user = async (req, res) => { label: `[[notifications:${type}]]`, })); res.render('admin/settings/user', { + title: '[[admin/menu:settings/user]]', notificationSettings: notificationSettings, }); }; @@ -45,6 +60,7 @@ settingsController.user = async (req, res) => { settingsController.post = async (req, res) => { const groupData = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1); res.render('admin/settings/post', { + title: '[[admin/menu:settings/post]]', groupsExemptFromPostQueue: groupData, }); }; @@ -52,22 +68,11 @@ settingsController.post = async (req, res) => { settingsController.advanced = async (req, res) => { const groupData = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1); res.render('admin/settings/advanced', { + title: '[[admin/menu:settings/advanced]]', groupsExemptFromMaintenanceMode: groupData, }); }; -settingsController.languages = async function (req, res) { - const languageData = await languages.list(); - languageData.forEach((language) => { - language.selected = language.code === meta.config.defaultLang; - }); - - res.render('admin/settings/languages', { - languages: languageData, - autoDetectLang: meta.config.autoDetectLang, - }); -}; - settingsController.navigation = async function (req, res) { const [admin, allGroups] = await Promise.all([ navigationAdmin.getAdmin(), @@ -94,23 +99,14 @@ settingsController.navigation = async function (req, res) { }); admin.navigation = admin.enabled.slice(); - + admin.title = '[[admin/menu:settings/navigation]]'; res.render('admin/settings/navigation', admin); }; -settingsController.homepage = async function (req, res) { - const routes = await helpers.getHomePageRoutes(req.uid); - res.render('admin/settings/homepage', { routes: routes }); -}; - -settingsController.social = async function (req, res) { - const posts = await social.getPostSharing(); - res.render('admin/settings/social', { - posts: posts, - }); -}; - settingsController.api = async (req, res) => { const tokens = await api.utils.tokens.list(); - res.render('admin/settings/api', { tokens }); + res.render('admin/settings/api', { + title: '[[admin/menu:settings/api]]', + tokens, + }); }; diff --git a/src/meta/css.js b/src/meta/css.js index a51c8d72a8..b667847242 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -31,10 +31,8 @@ const buildImports = { }, admin: function (source) { return [ - '@import "admin/vars";', - '@import "bootswatch/dist/materia/variables";', + '@import "admin/overrides";', '@import "bootstrap/scss/bootstrap";', - '@import "bootswatch/dist/materia/bootswatch";', '@import "mixins";', '@import "fontawesome";', '@import "@adactive/bootstrap-tagsinput/src/bootstrap-tagsinput";', diff --git a/src/meta/js.js b/src/meta/js.js index ccba341eaf..065ad2c1f0 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -83,6 +83,10 @@ JS.buildModules = async function () { JS.linkStatics = async function () { await fs.promises.rm(path.join(__dirname, '../../build/public/plugins'), { recursive: true, force: true }); + + plugins.staticDirs['core/inter'] = path.join(basePath, 'node_modules//@fontsource/inter/files'); + plugins.staticDirs['core/poppins'] = path.join(basePath, 'node_modules//@fontsource/poppins/files'); + await Promise.all(Object.keys(plugins.staticDirs).map(async (mappedPath) => { const sourceDir = plugins.staticDirs[mappedPath]; const destDir = path.join(__dirname, '../../build/public/plugins', mappedPath); diff --git a/src/posts/uploads.js b/src/posts/uploads.js index 1c23071c5d..c8c12999d5 100644 --- a/src/posts/uploads.js +++ b/src/posts/uploads.js @@ -142,6 +142,14 @@ module.exports = function (Posts) { filePaths = [filePaths]; } + if (process.platform === 'win32') { + // windows path => 'files\\1685368788211-1-profileimg.jpg' + // turn it into => 'files/1685368788211-1-profileimg.jpg' + filePaths.forEach((file) => { + file.path = file.path.split(path.sep).join(path.posix.sep); + }); + } + const keys = filePaths.map(fileObj => `upload:${md5(fileObj.path.replace('-resized', ''))}:pids`); return await Promise.all(keys.map(k => db.getSortedSetRange(k, 0, -1))); }; diff --git a/src/routes/admin.js b/src/routes/admin.js index c7e9c5f060..01e228dabe 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -35,10 +35,7 @@ module.exports = function (app, name, middleware, controllers) { helpers.setupAdminPageRoute(app, `/${name}/settings/user`, middlewares, controllers.admin.settings.user); helpers.setupAdminPageRoute(app, `/${name}/settings/post`, middlewares, controllers.admin.settings.post); helpers.setupAdminPageRoute(app, `/${name}/settings/advanced`, middlewares, controllers.admin.settings.advanced); - helpers.setupAdminPageRoute(app, `/${name}/settings/languages`, middlewares, controllers.admin.settings.languages); helpers.setupAdminPageRoute(app, `/${name}/settings/navigation`, middlewares, controllers.admin.settings.navigation); - helpers.setupAdminPageRoute(app, `/${name}/settings/homepage`, middlewares, controllers.admin.settings.homepage); - helpers.setupAdminPageRoute(app, `/${name}/settings/social`, middlewares, controllers.admin.settings.social); helpers.setupAdminPageRoute(app, `/${name}/settings/api`, middlewares, controllers.admin.settings.api); helpers.setupAdminPageRoute(app, `/${name}/settings/:term?`, middlewares, controllers.admin.settings.get); diff --git a/src/social.js b/src/social.js index c95f2cc3ac..2dd4045024 100644 --- a/src/social.js +++ b/src/social.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const plugins = require('./plugins'); const db = require('./database'); +const meta = require('./meta'); const social = module.exports; @@ -26,9 +27,8 @@ social.getPostSharing = async function () { }, ]; networks = await plugins.hooks.fire('filter:social.posts', networks); - const activated = await db.getSetMembers('social:posts.activated'); networks.forEach((network) => { - network.activated = activated.includes(network.id); + network.activated = parseInt(meta.config[`post-sharing-${network.id}`], 10) === 1; }); social.postSharing = networks; @@ -41,12 +41,16 @@ social.getActivePostSharing = async function () { }; social.setActivePostSharingNetworks = async function (networkIDs) { + // keeping for 1.0.0 upgrade script that uses this function social.postSharing = null; - await db.delete('social:posts.activated'); if (!networkIDs.length) { return; } - await db.setAdd('social:posts.activated', networkIDs); + const data = {}; + networkIDs.forEach((id) => { + data[`post-sharing-${id}`] = 1; + }); + await db.setObject('config', data); }; require('./promisify')(social); diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 1621ef71a0..21165713db 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -19,7 +19,6 @@ SocketAdmin.tags = require('./admin/tags'); SocketAdmin.rewards = require('./admin/rewards'); SocketAdmin.navigation = require('./admin/navigation'); SocketAdmin.rooms = require('./admin/rooms'); -SocketAdmin.social = require('./admin/social'); SocketAdmin.themes = require('./admin/themes'); SocketAdmin.plugins = require('./admin/plugins'); SocketAdmin.widgets = require('./admin/widgets'); diff --git a/src/socket.io/admin/social.js b/src/socket.io/admin/social.js deleted file mode 100644 index 378d736e89..0000000000 --- a/src/socket.io/admin/social.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -const social = require('../../social'); - -const SocketSocial = module.exports; - -SocketSocial.savePostSharingNetworks = async function (socket, data) { - await social.setActivePostSharingNetworks(data); -}; diff --git a/src/upgrades/3.2.0/migrate_post_sharing.js b/src/upgrades/3.2.0/migrate_post_sharing.js new file mode 100644 index 0000000000..661a9a259e --- /dev/null +++ b/src/upgrades/3.2.0/migrate_post_sharing.js @@ -0,0 +1,19 @@ +'use strict'; + +const db = require('../../database'); + +module.exports = { + name: 'Migrate post sharing values to config', + timestamp: Date.UTC(2023, 4, 23), + method: async () => { + const activated = await db.getSetMembers('social:posts.activated'); + if (activated.length) { + const data = {}; + activated.forEach((id) => { + data[`post-sharing-${id}`] = 1; + }); + await db.setObject('config', data); + await db.delete('social:posts.activated'); + } + }, +}; diff --git a/src/views/admin/advanced/cache.tpl b/src/views/admin/advanced/cache.tpl index af1c616847..4581b571b1 100644 --- a/src/views/admin/advanced/cache.tpl +++ b/src/views/admin/advanced/cache.tpl @@ -1,9 +1,9 @@ - -
    + +
    {{{each caches}}} -
    +
    [[admin/advanced/cache:{@key}-cache]]
    @@ -52,4 +52,4 @@
    - + diff --git a/src/views/admin/advanced/database.tpl b/src/views/admin/advanced/database.tpl index 311ff9deef..4e10d868d7 100644 --- a/src/views/admin/advanced/database.tpl +++ b/src/views/admin/advanced/database.tpl @@ -1,146 +1,147 @@ - -
    - {{{ if mongo }}} -
    - {{{ if mongo.serverStatusError }}} -
    - {mongo.serverStatusError} -
    - {{{ end }}} -
    -
    [[admin/advanced/database:mongo]]
    -
    -
    - [[admin/advanced/database:mongo.version]] {mongo.version}
    -
    - [[admin/advanced/database:uptime-seconds]] {mongo.uptime}
    - [[admin/advanced/database:mongo.storage-engine]] {mongo.storageEngine}
    - [[admin/advanced/database:mongo.collections]] {mongo.collections}
    - [[admin/advanced/database:mongo.objects]] {mongo.objects}
    - [[admin/advanced/database:mongo.avg-object-size]] [[admin/advanced/database:x-b, {mongo.avgObjSize}]]
    -
    - [[admin/advanced/database:mongo.data-size]] [[admin/advanced/database:x-gb, {mongo.dataSize}]]
    - [[admin/advanced/database:mongo.storage-size]] [[admin/advanced/database:x-gb, {mongo.storageSize}]]
    - [[admin/advanced/database:mongo.index-size]] [[admin/advanced/database:x-gb, {mongo.indexSize}]]
    - {{{ if mongo.fileSize }}} - [[admin/advanced/database:mongo.file-size]] [[admin/advanced/database:x-gb, {mongo.fileSize}]]
    - {{{ end }}} -
    - [[admin/advanced/database:mongo.resident-memory]] [[admin/advanced/database:x-gb, {mongo.mem.resident}]]
    - [[admin/advanced/database:mongo.virtual-memory]] [[admin/advanced/database:x-gb, {mongo.mem.virtual}]]
    - [[admin/advanced/database:mongo.mapped-memory]] [[admin/advanced/database:x-gb, {mongo.mem.mapped}]]
    -
    - [[admin/advanced/database:mongo.bytes-in]] [[admin/advanced/database:x-gb, {mongo.network.bytesIn}]]
    - [[admin/advanced/database:mongo.bytes-out]] [[admin/advanced/database:x-gb, {mongo.network.bytesOut}]]
    - [[admin/advanced/database:mongo.num-requests]] {mongo.network.numRequests}
    +
    +
    + {{{ if mongo }}} +
    + {{{ if mongo.serverStatusError }}} +
    + {mongo.serverStatusError} +
    + {{{ end }}} +
    +
    [[admin/advanced/database:mongo]]
    +
    +
    +
    [[admin/advanced/database:mongo.version]] {mongo.version}
    +
    +
    [[admin/advanced/database:uptime-seconds]] {mongo.uptime}
    +
    [[admin/advanced/database:mongo.storage-engine]] {mongo.storageEngine}
    +
    [[admin/advanced/database:mongo.collections]] {mongo.collections}
    +
    [[admin/advanced/database:mongo.objects]] {mongo.objects}
    +
    [[admin/advanced/database:mongo.avg-object-size]] [[admin/advanced/database:x-b, {mongo.avgObjSize}]]
    +
    +
    [[admin/advanced/database:mongo.data-size]] [[admin/advanced/database:x-gb, {mongo.dataSize}]]
    +
    [[admin/advanced/database:mongo.storage-size]] [[admin/advanced/database:x-gb, {mongo.storageSize}]]
    +
    [[admin/advanced/database:mongo.index-size]] [[admin/advanced/database:x-gb, {mongo.indexSize}]]
    + {{{ if mongo.fileSize }}} +
    [[admin/advanced/database:mongo.file-size]] [[admin/advanced/database:x-gb, {mongo.fileSize}]]
    + {{{ end }}} +
    +
    [[admin/advanced/database:mongo.resident-memory]] [[admin/advanced/database:x-gb, {mongo.mem.resident}]]
    +
    [[admin/advanced/database:mongo.virtual-memory]] [[admin/advanced/database:x-gb, {mongo.mem.virtual}]]
    +
    [[admin/advanced/database:mongo.mapped-memory]] [[admin/advanced/database:x-gb, {mongo.mem.mapped}]]
    +
    +
    [[admin/advanced/database:mongo.bytes-in]] [[admin/advanced/database:x-gb, {mongo.network.bytesIn}]]
    +
    [[admin/advanced/database:mongo.bytes-out]] [[admin/advanced/database:x-gb, {mongo.network.bytesOut}]]
    +
    [[admin/advanced/database:mongo.num-requests]] {mongo.network.numRequests}
    +
    -
    - {{{ end }}} + {{{ end }}} - {{{ if redis }}} -
    -
    -
    [[admin/advanced/database:redis]]
    -
    -
    - [[admin/advanced/database:redis.version]] {redis.redis_version}
    -
    - [[admin/advanced/database:uptime-seconds]] {redis.uptime_in_seconds}
    - [[admin/advanced/database:uptime-days]] {redis.uptime_in_days}
    -
    - [[admin/advanced/database:redis.keys]] {redis.keys}
    - [[admin/advanced/database:redis.expires]] {redis.expires}
    - [[admin/advanced/database:redis.avg-ttl]] {redis.avg_ttl}
    - [[admin/advanced/database:redis.connected-clients]] {redis.connected_clients}
    - [[admin/advanced/database:redis.connected-slaves]] {redis.connected_slaves}
    - [[admin/advanced/database:redis.blocked-clients]] {redis.blocked_clients}
    -
    + {{{ if redis }}} +
    +
    +
    [[admin/advanced/database:redis]]
    +
    +
    +
    [[admin/advanced/database:redis.version]] {redis.redis_version}
    +
    +
    [[admin/advanced/database:uptime-seconds]] {redis.uptime_in_seconds}
    +
    [[admin/advanced/database:uptime-days]] {redis.uptime_in_days}
    +
    +
    [[admin/advanced/database:redis.keys]] {redis.keys}
    +
    [[admin/advanced/database:redis.expires]] {redis.expires}
    +
    [[admin/advanced/database:redis.avg-ttl]] {redis.avg_ttl}
    +
    [[admin/advanced/database:redis.connected-clients]] {redis.connected_clients}
    +
    [[admin/advanced/database:redis.connected-slaves]] {redis.connected_slaves}
    +
    [[admin/advanced/database:redis.blocked-clients]] {redis.blocked_clients}
    +
    - [[admin/advanced/database:redis.used-memory]] [[admin/advanced/database:x-gb, {redis.used_memory_human}]]
    - [[admin/advanced/database:redis.memory-frag-ratio]] {redis.mem_fragmentation_ratio}
    -
    - [[admin/advanced/database:redis.total-connections-recieved]] {redis.total_connections_received}
    - [[admin/advanced/database:redis.total-commands-processed]] {redis.total_commands_processed}
    - [[admin/advanced/database:redis.iops]] {redis.instantaneous_ops_per_sec}
    +
    [[admin/advanced/database:redis.used-memory]] [[admin/advanced/database:x-gb, {redis.used_memory_human}]]
    +
    [[admin/advanced/database:redis.memory-frag-ratio]] {redis.mem_fragmentation_ratio}
    +
    +
    [[admin/advanced/database:redis.total-connections-recieved]] {redis.total_connections_received}
    +
    [[admin/advanced/database:redis.total-commands-processed]] {redis.total_commands_processed}
    +
    [[admin/advanced/database:redis.iops]] {redis.instantaneous_ops_per_sec}
    - [[admin/advanced/database:redis.iinput]] [[admin/advanced/database:x-mb, {redis.instantaneous_input}]]
    - [[admin/advanced/database:redis.ioutput]] [[admin/advanced/database:x-mb, {redis.instantaneous_output}]]
    - [[admin/advanced/database:redis.total-input]] [[admin/advanced/database:x-gb, {redis.total_net_input}]]
    - [[admin/advanced/database:redis.total-output]] [[admin/advanced/database:x-gb, {redis.total_net_output}]]
    +
    [[admin/advanced/database:redis.iinput]] [[admin/advanced/database:x-mb, {redis.instantaneous_input}]]
    +
    [[admin/advanced/database:redis.ioutput]] [[admin/advanced/database:x-mb, {redis.instantaneous_output}]]
    +
    [[admin/advanced/database:redis.total-input]] [[admin/advanced/database:x-gb, {redis.total_net_input}]]
    +
    [[admin/advanced/database:redis.total-output]] [[admin/advanced/database:x-gb, {redis.total_net_output}]]
    -
    - [[admin/advanced/database:redis.keyspace-hits]] {redis.keyspace_hits}
    - [[admin/advanced/database:redis.keyspace-misses]] {redis.keyspace_misses}
    +
    +
    [[admin/advanced/database:redis.keyspace-hits]] {redis.keyspace_hits}
    +
    [[admin/advanced/database:redis.keyspace-misses]] {redis.keyspace_misses}
    +
    -
    - {{{ end }}} + {{{ end }}} - {{{ if postgres }}} -
    -
    -
    [[admin/advanced/database:postgres]]
    -
    -
    - [[admin/advanced/database:postgres.version]] {postgres.version}
    -
    - [[admin/advanced/database:uptime-seconds]] {postgres.uptime}
    + {{{ if postgres }}} +
    +
    +
    [[admin/advanced/database:postgres]]
    +
    +
    +
    [[admin/advanced/database:postgres.version]] {postgres.version}
    +
    +
    [[admin/advanced/database:uptime-seconds]] {postgres.uptime}
    +
    + {{{ end }}}
    - {{{ end }}} -
    -
    - {{{ if mongo }}} -
    -
    -
    - [[admin/advanced/database:mongo.raw-info]] -
    +
    + {{{ if mongo }}} +
    +
    +
    + [[admin/advanced/database:mongo.raw-info]] +
    -
    -
    -
    {mongo.raw}
    +
    +
    +
    {mongo.raw}
    +
    -
    - {{{ end }}} + {{{ end }}} - {{{ if redis }}} -
    -
    -
    - [[admin/advanced/database:redis.raw-info]] -
    + {{{ if redis }}} +
    +
    +
    + [[admin/advanced/database:redis.raw-info]] +
    -
    -
    -
    {redis.raw}
    +
    +
    +
    {redis.raw}
    +
    -
    - {{{ end }}} + {{{ end }}} - {{{ if postgres }}} -
    -
    -
    - [[admin/advanced/database:postgres.raw-info]] -
    + {{{ if postgres }}} +
    +
    +
    + [[admin/advanced/database:postgres.raw-info]] +
    -
    -
    -
    {postgres.raw}
    +
    +
    +
    {postgres.raw}
    +
    + {{{ end }}}
    - {{{ end }}} -
    +
    \ No newline at end of file diff --git a/src/views/admin/advanced/errors.tpl b/src/views/admin/advanced/errors.tpl index bf70584e5a..683b835fee 100644 --- a/src/views/admin/advanced/errors.tpl +++ b/src/views/admin/advanced/errors.tpl @@ -1,77 +1,79 @@ -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    -
    -
    -
    -
    -
    [[admin/advanced/errors:manage-error-log]]
    -
    -
    - - [[admin/advanced/errors:export-error-log]] - - +
    +
    +
    [[admin/advanced/errors:manage-error-log]]
    +
    +
    + + [[admin/advanced/errors:export-error-log]] + + +
    -
    -
    -
    -
    -
    - [[admin/advanced/errors:error.404]] -
    -
    - - - - - - - {{{ each not-found }}} - - - - - {{{ end }}} - {{{ if !not-found.length }}} - - - - {{{ end }}} - -
    [[admin/advanced/errors:route]][[admin/advanced/errors:count]]
    {./value}{./score}
    -
    - [[admin/advanced/errors:no-routes-not-found]] -
    -
    +
    +
    +
    +
    + [[admin/advanced/errors:error.404]] +
    +
    + + + + + + + {{{ each not-found }}} + + + + + {{{ end }}} + {{{ if !not-found.length }}} + + + + {{{ end }}} + +
    [[admin/advanced/errors:route]][[admin/advanced/errors:count]]
    {./value}{./score}
    +
    + [[admin/advanced/errors:no-routes-not-found]] +
    +
    +
    diff --git a/src/views/admin/advanced/events.tpl b/src/views/admin/advanced/events.tpl index d5d0fa8022..73b39613c8 100644 --- a/src/views/admin/advanced/events.tpl +++ b/src/views/admin/advanced/events.tpl @@ -1,4 +1,4 @@ -
    +
    [[admin/advanced/events:events]]
    {{{ if !events.length }}} @@ -6,25 +6,27 @@ {{{ end }}}
    {{{ each events }}} -
    +
    -
    -
    +
    +
    #{events.eid} {events.type} uid {events.uid} {{{ if events.ip }}}{events.ip}{{{ end }}} {buildAvatar(events.user, "24px", true)} {events.user.username} - - {events.timestampISO} + {events.timestampISO} +
    +
    +
    -
    {events.jsonString}
    +
    {events.jsonString}
    {{{ end }}} - +
    diff --git a/src/views/admin/advanced/hooks.tpl b/src/views/admin/advanced/hooks.tpl index d137038ef6..357d3039f5 100644 --- a/src/views/admin/advanced/hooks.tpl +++ b/src/views/admin/advanced/hooks.tpl @@ -1,9 +1,9 @@ -
    +
    {{{ each hooks }}}
    diff --git a/src/views/admin/advanced/logs.tpl b/src/views/admin/advanced/logs.tpl index 73bd209486..8d8f049d5a 100644 --- a/src/views/admin/advanced/logs.tpl +++ b/src/views/admin/advanced/logs.tpl @@ -1,4 +1,4 @@ -
    +
    [[admin/advanced/logs:logs]]
    diff --git a/src/views/admin/appearance/customise.tpl b/src/views/admin/appearance/customise.tpl index f01359cb4f..3170480182 100644 --- a/src/views/admin/appearance/customise.tpl +++ b/src/views/admin/appearance/customise.tpl @@ -1,4 +1,4 @@ -
    +